DCSIMG
Cross-Origin Resource Sharing (CORS) and WCF - Ido Flatow's Blog Veni Vidi Scripsi

Ido Flatow's Blog

Veni Vidi Scripsi

News

Have you heard me speak?
Powered
<style type='text/css' media='screen' id='sm_css'> #smix {overflow: visible;height: auto;border-radius: 10px;max-width: 250px;background-color: #323232;text-align: left;font-size: 12px;line-height: 16px;font-family:'Lucida Sans Unicode','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;-webkit-border-radius: 10px;-moz-border-radius: 10px;border-radius: 10px;} #smix a {color: #0056CC;text-decoration: none;} #smix .sm_head {color: #fff; line-height: 1em;font-size: 1.4em;padding: 10px;color: #fff;} #smix .sm_lanyard_wrapper {background-color: #fff;;clear: both;width: 97%;margin: 0 auto;margin-bottom: 0px;} #smix .sm_lanyard_content {padding: 7px;}#smix button.sm_rec, #smix a.sm_rec, #smix input[type=submit].sm_rec { padding: 6px 10px; -webkit-border-radius: 2px 2px;-moz-border-radius: 2px; border-radius: 2px; border: solid 1px rgb(153, 153, 153); background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(255, 255, 255)), to(rgb(221, 221, 221))); color: #333; text-decoration: none; cursor: pointer; display: inline-block; text-align: center; text-shadow: 0px 1px 1px rgba(255,255,255,1); line-height: 1; }#smix .sm_rec:hover { background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(248, 248, 248)), to(rgb(221, 221, 221))); }#smix .sm_rec:active { background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(204, 204, 204)), to(rgb(221, 221, 221))); }#smix .sm_rec.medium { padding: 3px 7px; font-size: 13px; }#smix .sm_rec span.icon.thumbs_up {background-position: 0px 36px;vertical-align: text-top;display: inline-block;margin-right: 4px;height: 18px;width: 16px;background-image: url(http://speakermix.com/images/new/thumbsold.png);}#smix .sm_rec:hover span.icon.thumbs_up {background-position: 0px 18px;} #smix .sm_events {padding:2px 0px 4px 0px;} #smix .sm_section {font-size: 10px; border-bottom: 1px solid silver; margin-bottom: 6px;} #smix .sm_subline {font-size:120%;margin-top:4px;font-weight:bold} #smix .powered {text-align: right} #smix .powered img {margin: 7px} </style>
Sela Technology Center

Advertisement

Cross-Origin Resource Sharing (CORS) and WCF

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

Comments

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# July 4, 2011 6:05 PM

Patrick said:

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.

# October 20, 2011 5:10 AM

Ido Flatow said:

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

# October 20, 2011 6:07 AM

Eric said:

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

# November 2, 2011 9:11 PM

Ido Flatow said:

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).

# November 3, 2011 9:20 AM

Lalith Tenneti said:

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

Thanks

# December 14, 2011 12:17 AM

Ido Flatow said:

Try the direct link:

skydrive.live.com

# December 14, 2011 10:15 AM

Lalith Tenneti said:

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.

# December 16, 2011 12:27 AM

Molly said:

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?

# February 22, 2012 1:23 PM

Ido Flatow said:

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.

# February 22, 2012 7:45 PM

Eddie Chu said:

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

# May 8, 2012 2:29 PM

Konstantin said:

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 developer.mozilla.org/.../HTTP_access_control the rule for header is:

Access-Control-Request-Headers: <field-name>[, <field-name>]*

# May 10, 2012 3:41 PM

peteski said:

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? :(

# July 2, 2012 5:18 PM

Obaid said:

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");

# July 16, 2012 4:47 PM

Singing said:

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?

# September 18, 2012 2:06 AM

Ido Flatow said:

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

# September 18, 2012 9:44 AM

Steve DuMosch said:

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: "localhost/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.

# October 2, 2012 4:40 PM

Steve DuMosch said:

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.

# October 3, 2012 8:45 AM

Nishant said:

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?  

# November 6, 2012 2:22 AM

Smothers said:

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.

# February 2, 2013 10:21 AM

David said:

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

# March 6, 2013 11:20 AM

elipleNub said:

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=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=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=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.

# April 21, 2013 6:39 PM

XRumerTest said:

Hello. And Bye.

# April 23, 2013 10:02 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: