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:
-
You need to repeat these lines in each of your service methods.
-
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:
- The code that returns the message with the headers is located in the behavior itself – no need to place it in each service method.
- The behavior catches the OPTIONS request, and does not require running the service method twice.
- 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.
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.
Patrick, thanks for finding the JSON bug. The fixed code is available in my skydrive (same link)
How do you handle authentication? If the endpoint requires authentication IIS sends 401 BEFORE the actual behavior kicks in…
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).
I cannot access your skydrive link. Canyou please send the code to lalithkx@hotmail.com ?
Thanks
Try the direct link:
https://skydrive.live.com/?cid=5ef5be1ab30a6056&id=5EF5BE1AB30A6056%21283
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.
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?
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.
Great post! This solve my problem perfectly! Thanks a lot!!!
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:
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? 🙁
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”);
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?
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
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.
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.
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?
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.
How do I use the factory you provided with IIS? Setup instructions?
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.
Hello. And Bye.
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!
Hi,
What happens when the Factory is already “taken”? I have there ninject.
Regards.
You saved my day, even my week !
You created very usefull classes.
Again : Thanks a lot!!!
Thanks for this code. Is there a particular license under which this is released?
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.
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
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?
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