Cross-Origin Resource Sharing (CORS) and WCF

July 2, 2011

Yesterday I encountered the following question in the MSDN forums about calling a cross-domain WCF RESTful service from Chrome.

The problem with doing the above is because there is a cross-domain call to a WCF service. When you are using Chrome, the browser tries to find out if it can do the call by sending a cross-origin resource sharing (CORS) request, as explained in this article. In IE, you will most likely get a cross-domain exception.

After searching the web a bit, I found that the immediate solution is to change the supported HTTP method of the operation to “*”, and to add the special cross-origin headers to the outputted response, like so:

WebOperationContext.Current.OutgoingResponse.Headers.Add(
  "Access-Control-Allow-Origin", "*"); WebOperationContext.Current.OutgoingResponse.Headers.Add(
  "Access-Control-Allow-Methods", "POST"); WebOperationContext.Current.OutgoingResponse.Headers.Add(
  "Access-Control-Allow-Headers", "Content-Type, Accept");

There are two problems with this solution:

  1. You need to repeat these lines in each of your service methods.
  2. The service method is called twice, once for the “preflight” request (the request with the OPTIONS HTTP verb), and a second time for the invocation itself.

So I took the time and written a special endpoint behavior that can be attached to any webHttpBinding based endpoint.

The code does the following:

1. Every response message gets the “Access-Control-Allow-Origin” header with the value of “*”, to tell the client that the service allowed the request from the client.

2. If the client sends a request to the service with the verb OPTIONS (this is referred to as a “preflight” request), the service will automatically return a 200 (ok) response with the required headers: “Access-Control-Allow-Headers” and “Access-Control-Allow-Methods” (in addition to the allow-origin header). Clients that look for the “allow” headers in the response will then send the original required request to the service.

The benefits of using this behavior is:

  1. The code that returns the message with the headers is located in the behavior itself – no need to place it in each service method.
  2. The behavior catches the OPTIONS request, and does not require running the service method twice.
  3. You do not need to change any part of your service contract or service implementation. The behavior is only needed to be applied using configuration.

To implement this behavior I used a message inspector that checks the request and changes the response, and an operation invoker that wraps the unhandled operation invoker. The custom operation invoker handles the OPTIONS message requests, which otherwise would have caused the service to return an error message (because no method is set to handle “Options” requests).

The endpoint behavior can be attached to the endpoint through configuration, like so:

<behaviors>

  <endpointBehaviors>

    <behavior name="webSupport">

      <webHttp />

      <CorsSupport />

    </behavior>

  </endpointBehaviors>

</behaviors>

<extensions>

  <behaviorExtensions>

    <add name="CorsSupport" type="WebHttpCors.CorsSupportBehaviorElement, WebHttpCors, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

  </behaviorExtensions>

</extensions>

<services>

  <service name="Service.JSonService">

    <endpoint address="http://localhost:8080" behaviorConfiguration="webSupport” binding="webHttpBinding" contract="Service.IJSonService" />

  </service>

</services>

Or, if you are using IIS to host your services, I also created a service host factory that creates a WebServiceHost and adds the behavior to every webHttpBinding based endpoint created by the host. To use it in your .svc file, just write something like this:

<%@ ServiceHost Language="C#" Debug="true" Service="Service.JSonService, Service" Factory="WebHttpCors.CorsWebServiceHostFactory, WebHttpCors" %>

You can download the behavior code + two sample hosts (IIS and self-hosted) that shows how to use the behavior from my skydrive.

To test the two hosts, open the webform1.aspx and change the target URL to access the IIS/self-hosted service.

Enjoy.

kick it on DotNetKicks.com

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

30 comments

  1. PatrickOctober 20, 2011 ב 5:10 am

    Huge thanks for this Ido! The sample project works perfectly and we are now using CORS in all our JSON endpoints. Goodbye jsonp hacks!

    If anyone else is using this for json modify BeforeSendReply and add the header Content-Type: application/json and your browsers will be happier.

    Reply
  2. Ido FlatowOctober 20, 2011 ב 6:07 am

    Patrick, thanks for finding the JSON bug. The fixed code is available in my skydrive (same link)

    Reply
  3. EricNovember 2, 2011 ב 9:11 pm

    How do you handle authentication? If the endpoint requires authentication IIS sends 401 BEFORE the actual behavior kicks in…

    Reply
  4. Ido FlatowNovember 3, 2011 ב 9:20 am

    401s are usually handled by the client communication stack – in browsers if a 401 is received the browser will usually handle it by asking for a username/password or passing your current windows credentials (depends on which authentication type is required). If the browser cannot handle 401s then that is a problem (I think some JSONP scenarios don’t support authentication on the server side).

    Reply
  5. Lalith TennetiDecember 14, 2011 ב 12:17 am

    I cannot access your skydrive link. Canyou please send the code to lalithkx@hotmail.com ?

    Thanks

    Reply
  6. Lalith TennetiDecember 16, 2011 ב 12:27 am

    Thanks Ido:

    I was able toaccess it from my home. I thing our company firewalls were blocking the link.
    Though I was able to build it mostly on my own, it helped fix a few issues.

    Reply
  7. MollyFebruary 22, 2012 ב 1:23 pm

    Hi,

    i tried using the WebHttpCors in my WCF project but I get error “cannot add the behavior extension ‘CorsSupport’ to the service behavior named ‘WcfService1.ServiceBehaviour’ because the underlying behavior type does not implement the IServiceBehavior interface.”

    Can you please help me resolve the issue?

    Reply
  8. Ido FlatowFebruary 22, 2012 ב 7:45 pm

    Hi Molly,
    I would like to inspect your code. Any chance you can contact me through my blog’s contact page, so we can continue this on mail?

    Ido.

    Reply
  9. Eddie ChuMay 8, 2012 ב 2:29 pm

    Great post! This solve my problem perfectly! Thanks a lot!!!

    Reply
  10. KonstantinMay 10, 2012 ב 3:41 pm

    Thank you for sharing this library. I’ve added a couple of lines to make it work for me (I am trying to connect WCF REST service and jQuery client).
    1. Added content-type = json header for OPTIONS response
    2. Instead of “*” value in Access-Control-Allow-Methods header I’ve set “POST, PUT, GET, OPTIONS, DELETE” as acording to this manual https://developer.mozilla.org/En/HTTP_access_control the rule for header is:
    Access-Control-Request-Headers: [, ]*

    Reply
  11. peteskiJuly 2, 2012 ב 5:18 pm

    Is it possible to use this behavior when you have a transport security on the binding of NTLM? Will the OPTIONS header have to negotiate with user credentials too? 🙁

    Reply
  12. ObaidJuly 16, 2012 ב 4:47 pm

    If the Access-Control-Max-Age header is added to the pre-flight response then the browser will not make pre-flight requests everytime.

    property.Headers.Add(“Access-Control-Max-Age”, “1728000”);

    Reply
  13. SingingSeptember 18, 2012 ב 2:06 am

    Ido,

    I tried your method. My ErrorHandler throws an null reference exception in the CustomOperationInvoker Invoke function where it finds it is a preflight invoke and returns null. Any idea what went wrong?

    Reply
  14. Ido FlatowSeptember 18, 2012 ב 9:44 am

    Hi Singing,
    Can you please send me a sample of your code so I can check why it returns null? you can send it to idof@sela.co.il

    Reply
  15. Steve DuMoschOctober 2, 2012 ב 4:40 pm

    Huge, huge thanks for this sample. I was in desperate need of a windows service hosted WCF rest service (not IIS). It works perfectly for receiving information. However, I need to pass information to the server. I’ve tried adding a second method to the interface in your example:

    [WebInvoke(Method = “POST”, BodyStyle = WebMessageBodyStyle.WrappedRequest, ResponseFormat = WebMessageFormat.Json, RequestFormat= WebMessageFormat.Json)]
    [OperationContract]
    string Echo2(string input);

    With the following implementation:

    public string Echo2(string input)
    {
    return input;
    }

    I’m then attempting to call it via:
    $.ajax({
    type: “POST”,
    url: “http://localhost:8080/Echo2”,
    contentType: “application/json”,
    datatype: “text/json”,
    data: “{‘input’ : ‘test’}”,
    success: function (str) {
    alert (str);
    },
    error: function (xhr) {
    alert(xhr.responseText);
    }
    });

    But I just get a server error 400. How can i make this work? I’m hoping i’m missing something simple.

    Many thanks in advance.

    Reply
  16. Steve DuMoschOctober 3, 2012 ב 8:45 am

    SOLVED!

    It was merely a problem of my AJAX data syntax. I needed to swap the single and double quotes.

    data: ‘{“input” : “test”}’

    I hope this helps someone in the future.

    Reply
  17. NishantNovember 6, 2012 ב 2:22 am

    As per my understanding, the solution described in the post would work only if authentication check is turned off on the binding. I have a WCF service hosted using windows service with basic authentication enabled using binding.

    IDispatchMessageInspector seems to be invoked only after authentication is done so the code never gets invoked for pre-flight request as it just returns with 401 and does nothing after that. I can see 401 through fiddler. If I set client credentials to none on the binding it works but then I lose authentication capabilities. Did you find a way to overcome this?

    Reply
  18. SmothersFebruary 2, 2013 ב 10:21 am

    My relatives every time say that I am wasting my time
    here at net, however I know I am getting know-how daily by reading such good
    posts.

    Reply
  19. DavidMarch 6, 2013 ב 11:20 am

    How do I use the factory you provided with IIS? Setup instructions?

    Reply
  20. elipleNubApril 21, 2013 ב 6:39 pm

    Nike Manumitted TR Suited 3 noteworthy features is to from the additional scheme: Nike Let off 5 soles improved bending Groove; stylish tractor formation making training more focused when; lighter preponderance, the permeability is stronger, and more fashionable shoe designs not not exhort shoes [url=http://turbo-vac.co.uk/components_13.cfm]nike free uk[/url]
    more serene wearing, barefoot training caress, but also more stylish appearance.

    Nike Manumitted TR Fit 3 provides first-class lateral reliability, you can be suffering with the legs in the lap boost during training. Acrid vamp majuscule letters breathable webbing, disgrace soap up’s one of a kind map can be [url=http://markwarren.org.uk/property-waet.cfm]air max 90 uk[/url]
    seen from stem to stern it. Lightweight, ragged, piddling froth matter habituated to past merest occasional seams, more obedient, support is stronger. Need more mainstay, part of a training irritate, foam close in more parts of the shortage championing conformableness, foam loose. Throw away twice tongue moisture wicking mock materials, unshiny on your feet, help keep feet sear and comfortable. Phylite [url=http://hear4you.com/catalogues.cfm]air max 1 uk[/url]
    midsole offers lightweight shock unchanging, famous durability and even outsole can do to greatly reduce the comprehensive dialect heft of the shoe. Qianzhang pods on the outsole and heel-shaped Green rubber enhances the shoe multi-directional drag on different surfaces.

    Reply
  21. XRumerTestApril 23, 2013 ב 10:02 pm

    Hello. And Bye.

    Reply
  22. FluhchoonMay 30, 2013 ב 8:01 am

    om citing or relying on opinions not certified for [url=http://mbt-shoes.safecranes.org]mbt shoe[/url] publication or ordered published, except as specified by rule 8.
    welcome our[0nline shop! ces, the severity of Sandras injuries substantially supported an inference that defendant intended to torture her.

    http://mbt.outlet-jewels.com/ [Online shop!

    Reply
  23. MeJuly 23, 2013 ב 11:45 pm

    Hi,

    What happens when the Factory is already “taken”? I have there ninject.

    Regards.

    Reply
  24. RoroSeptember 26, 2013 ב 5:11 am

    You saved my day, even my week !
    You created very usefull classes.
    Again : Thanks a lot!!!

    Reply
  25. Myles HendersonFebruary 13, 2014 ב 9:56 pm

    Thanks for this code. Is there a particular license under which this is released?

    Reply
    1. Ido Flatow
      Ido FlatowFebruary 13, 2014 ב 10:02 pm

      It’s free to use, as with all of my sample code 🙂
      If you’re mentioning it in an article, book, or presentation, I would appreciate putting in a link to my blog.

      Reply
      1. SubroFebruary 17, 2014 ב 12:14 am

        Dear Ido,

        First let me thank you for this elegant solution…you really helped me understand about cross domain concept.

        It is working fine but recently i started getting
        pending status on 101 switching protocols.

        Request Method:GET
        Status Code:101 Switching Protocols
        Request Headers CAUTION: Provisional headers are shown.
        Cache-Control:no-cache
        Connection:Upgrade
        Host:localhost:19956
        Origin:http://localhost:51436
        Pragma:no-cache
        Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits, x-webkit-deflate-frame
        Sec-WebSocket-Key:NBDIQOAdGfbzTnUZWgIcHQ==
        Sec-WebSocket-Version:13
        Upgrade:websocket
        User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36

        Reply
        1. Ido Flatow
          Ido FlatowFebruary 18, 2014 ב 9:01 am

          The response you are showing is part of the WebSockets connection upgrade. You aren’t supposed to get this message if you are connecting to a WebHttpBinding.
          Are you by any chance calling to a service that uses NetHttpBinding instead of WebHttpBinding?

          Reply
          1. SubroFebruary 19, 2014 ב 4:06 am

            Thanks Ido for your valuable input..
            I fixed my bindings..but no matter how much length i set…

            I am unable to fix my length issue for azure wcf service when locally hosted.

            I get this 413 (Request Entity Too Large) Microsoft-IIS/8.0.
            Can i send my web.config so you can guide me where i am missing..it is a very simple service but it send pictures in to database.

            Thanks,
            Subro