6

I have seen so many questions on SO about this, but I can't find what is missing in my code.

I'm implementing CORS cause I don't want to use JSONP.

I know this is a preflighted request and I think I'm adding the correct headers.

The error is that the website doesn't seem to like my WCF and every time I do a request, an OPTION method called is made even tho I have the Access-Control-Allow-Methods header.

I just want to make a POST call to my WCF with a contentType: "application/json",

The WCF is self hosted, the web application is on IIS 7.5.


What Chrome shows:

enter image description here


What fiddler shows enter image description here


Contract

<OperationContract()>
<WebInvoke(Method:="POST",
           RequestFormat:=WebMessageFormat.Json,
           ResponseFormat:=WebMessageFormat.Json,
           BodyStyle:=WebMessageBodyStyle.WrappedRequest)>
<FaultContract(GetType(ServiceFault))>
Function LookUpPerson(person As Person) _
                     As List(Of Person)

app.config

<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<bindings>
  <webHttpBinding>
    <binding name="webHttpBindingWithJsonP"
             crossDomainScriptAccessEnabled="true">
      <security mode="None" />
    </binding>
  </webHttpBinding>
</bindings>

<services>
  <service name="Project.Services.Person">  
    <endpoint address="ws"  binding="wsHttpBinding"     contract="Project.Services.Interfaces.IPublic" />   
    <endpoint address=""    binding="webHttpBinding"    contract="Project.Services.Interfaces.IPublic"
              behaviorConfiguration="jsonBehavior"/>
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:8732/" />
      </baseAddresses>
    </host>
  </service>
</services>

<extensions>
  <behaviorExtensions>
    <add name="customHeaders"
         type="Project.Services.Utilities.EnableCrossOriginResourceSharingBehavior, Project.Services, Version=1.0.0.0, Culture=neutral"/>
  </behaviorExtensions>
</extensions>

<endpointBehaviors>
  <behavior name="jsonBehavior">
    <webHttp/>
    <customHeaders />
  </behavior>
</endpointBehaviors>

javascript

$.ajax({
    url: "http://192.168.0.61:8282/Project.Services.Person/LookUpPerson",
    type: "POST",
    contentType: "application/json",
    crossDomain: true,
    dataType: "json",
    data: { person: JSON.stringify(person) },
    success: function (data) {
        // doing something
    },
    error: function (error) {
        // doing something
    }
});

On the WCF I have the following handlers, according to http://enable-cors.org/server_wcf.html

Public Class CustomHeaderMessageInspector
        Implements IDispatchMessageInspector

        Private requiredHeaders As Dictionary(Of String, String)
        Public Sub New(headers As Dictionary(Of String, String))
            requiredHeaders = If(headers, New Dictionary(Of String, String)())
        End Sub

        Public Function AfterReceiveRequest(ByRef request As System.ServiceModel.Channels.Message,
                                            channel As System.ServiceModel.IClientChannel,
                                            instanceContext As System.ServiceModel.InstanceContext) _
                                        As Object _
                                        Implements System.ServiceModel.Dispatcher.IDispatchMessageInspector.AfterReceiveRequest
            Return Nothing
        End Function

        Public Sub BeforeSendReply(ByRef reply As System.ServiceModel.Channels.Message,
                                   correlationState As Object) _
                               Implements System.ServiceModel.Dispatcher.IDispatchMessageInspector.BeforeSendReply
            Dim httpHeader = TryCast(reply.Properties("httpResponse"), HttpResponseMessageProperty)
            For Each item In requiredHeaders
                httpHeader.Headers.Add(item.Key, item.Value)
            Next
        End Sub

    End Class

And

Public Class EnableCrossOriginResourceSharingBehavior
        Inherits BehaviorExtensionElement
        Implements IEndpointBehavior

        Public Sub AddBindingParameters(endpoint As ServiceEndpoint, bindingParameters As System.ServiceModel.Channels.BindingParameterCollection) _
                                        Implements System.ServiceModel.Description.IEndpointBehavior.AddBindingParameters

        End Sub

        Public Sub ApplyClientBehavior(endpoint As ServiceEndpoint, clientRuntime As System.ServiceModel.Dispatcher.ClientRuntime) _
                                         Implements System.ServiceModel.Description.IEndpointBehavior.ApplyClientBehavior

        End Sub

        Public Sub ApplyDispatchBehavior(endpoint As ServiceEndpoint, endpointDispatcher As System.ServiceModel.Dispatcher.EndpointDispatcher) _
                                        Implements System.ServiceModel.Description.IEndpointBehavior.ApplyDispatchBehavior
            Dim requiredHeaders = New Dictionary(Of String, String)()

            requiredHeaders.Add("Access-Control-Allow-Origin", "*")
            requiredHeaders.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
            requiredHeaders.Add("Access-Control-Allow-Headers", "Origin, Content-Type, Accept")
            requiredHeaders.Add("Access-Control-Max-Age", "1728000")

            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(New CustomHeaderMessageInspector(requiredHeaders))
        End Sub

        Public Sub Validate(endpoint As ServiceEndpoint) _
            Implements System.ServiceModel.Description.IEndpointBehavior.Validate

        End Sub

        Public Overrides ReadOnly Property BehaviorType() As Type
            Get
                Return GetType(EnableCrossOriginResourceSharingBehavior)
            End Get
        End Property

        Protected Overrides Function CreateBehavior() As Object
            Return New EnableCrossOriginResourceSharingBehavior()
        End Function

    End Class

Sorry for the long post, I wanted to be specific.

Thanks in advance.


UPDATE

If I use contentType: "text/plain" I get the error on the chrome console:

POST http://192.168.0.61:8282/Project.Services.Person/LookUpPerson 400 (Bad Request)

Luis
  • 5,786
  • 8
  • 43
  • 62
  • Could'nt find anything good to help but found [this](http://www.israelaece.com/post/Introducao-ao-CORS.aspx) post about CORS where the data sent in the `OPTIONS` is different than your. Maybe it give you a clue but no sure if those are synonyms for the same thing you wrote. – Vitor Canova May 06 '14 at 12:03
  • @VitorCanova thanks, gonna check it out. – Luis May 06 '14 at 12:08
  • I haven't done this with WCF but I have with WebAPI. When I got 400 Bad Request it was because I was throwing an exception inside my method on the server. Are you able to attach a debugger to the WCF code and see the request and step through? – Erik Noren May 06 '14 at 16:43
  • @ErikNoren Yes, I have a breakpoint on the method, but its not being triggered – Luis May 06 '14 at 16:48
  • http://stackoverflow.com/questions/17333013/jquery-ajax-post-request-throws-405-method-not-allowed-on-restful-wcf – D Mishra May 07 '14 at 04:54
  • @DeepakMishra already seen it. Doesn't help much, it says `How to solve it? The simplest way is to enable CORS`, I have done it, but it's still not working. – Luis May 07 '14 at 12:02

1 Answers1

10

I had the same issue and this fixed it for me.

Change

[WebInvoke(Method = "Post")]

to

[WebInvoke(Method = "*")]

because although you accept POST, modern browsers will always send OPTIONS.

Sebastian Zartner
  • 18,808
  • 10
  • 90
  • 132
Pierrick
  • 324
  • 1
  • 7
  • 16
  • With `"*"` It allows me to reach the server, but I'm getting a new error with a bad request. Thanks tho – Luis Aug 14 '14 at 00:06
  • Once you get this far (correctly handling CORS requests etc), you still need one more item. Some code like `if (WebOperationContext.Current.IncomingRequest.Method == "OPTIONS") return null;` in the service method so that it will not abort (generate a 400 response) during the pre-flight call. – SOHO Developer Oct 24 '16 at 13:17