Sunday, June 17, 2012
CORS and RESTful WCF
I'm writing this post to hopefully save someone the hours of pain I had to go through when making CORS work with a RESTful WCF service. If you're reading this, you either know me or know what CORS is and are struggling with it. If you're reading this because you know me, you can probably tune out right now.
Let me start by saying a few things. 1) CORS is a pain in the rear. 2) Many of the 'solutions' I've found online are light on detail or leave out critical tidbits of information that could cause you to rip your hair out. I'm a glutton for punishment and prefer to implement such solutions in my own code.
If you're stuck dealing with CORS on a RESTful WCF service, please find below an example that doesn't require web.config changes.
First, open IIS Manager. Navigate to your application. In the feature view, under 'IIS':
- Open 'Handler Mappings' and change the 'Path' associated with OPTIONSVerbHandler to '*.foo' or something equally useless
- While you're in IIS Manager, it may be helpful to do something similar for the WebDAV-related handlers, given that you'd probably like to be able to use PUT and DELETE
Second, get into your code. The snippet below implements a simple OPTIONS handler that tells the requesting browser everything it needs to here. If you're wondering why I'm using a 'Stream' response type, it's because I can't stand the serializer/deserializer that is included (I rolled my own).
In your service contract:
[WebInvoke(Method = "OPTIONS", UriTemplate = "/*", BodyStyle = WebMessageBodyStyle.Bare)]
And the body of http_webinvoke_options():
public Stream http_webinvoke_options()
WebOperationContext web_op_context = WebOperationContext.Current;
OperationContext op_context = OperationContext.Current;
WebHeaderCollection header_list = web_op_context.IncomingRequest.Headers;
web_op_context.OutgoingResponse.StatusDescription = "OK";
web_op_context.OutgoingResponse.StatusCode = HttpStatusCode.OK;
web_op_context.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "OPTIONS, POST, GET, PUT, DELETE");
string headers = "";
headers += "Content-Type, ";
headers += "X-Requested-With, ";
headers += "Accept";
web_op_context.OutgoingResponse.Headers.Add("Accept-Language", "en-US, en");
web_op_context.OutgoingResponse.Headers.Add("Accept-Charset", "ISO-8859-1, utf-8");
string url = op_context.IncomingMessageHeaders.To.ToString();
url = url.Replace(op_context.IncomingMessageHeaders.To.PathAndQuery, "");
For your other code files, make sure that you add the 'Access-Control-Allow-Origin' header to each of them as well. It's not just about the OPTIONS response - each call needs to have this.
[WebInvoke(Method = "POST", UriTemplate = "/*", BodyStyle = WebMessageBodyStyle.Bare)]
Stream http_webinvoke_post(Stream body_stream);
And just prior to the response in the http_webinvoke_post method...
I hope this brings at least one person some relief.