Udostępnij za pośrednictwem


Implementing CORS support in WCF

The code for this post can be downloaded from the MSDN Code Gallery.

A pair of popular posts which I did a couple months back was to show how one can implement CORS (Cross-Origin Resource Sharing) in the net ASP.NET Web API framework. This week I found a couple of posts in the WCF forums from a user who wanted to make cross-domain calls to a WCF REST service. They were trying to use JSONP, but it didn’t work because the request needed to be made using non-GET verbs. So let’s try to implement the same support which we did fairly easily in the new API in WCF.

Cross-domain calls

A quick recap about the problem: in order to prevent malicious sites to “stealing” cookies from good sites and using them to get access to protected resources (imagine going to a bad site, and scripts in that site accessing your online bank and transferring your money elsewhere), browsers block by default AJAX requests going to domains other than the one where the HTML page originated. This is good for security purposes, but it blocks some valid scenarios, such as mash-up applications which gather data from many sources. There are some alternatives to make this scenario work, including JSONP (JSON with Padding), or using a separate “proxy” service on the same domain as the page to route the requests to the destination server. But those approaches have limitations: JSONP only works for GET requests (since it uses the <script> element in the HTML DOM, and proxy services need to be deployed on many places and add another level of indirection (and point of failure) to the system.

CORS (Cross-Origin Resource Sharing) is a new specification which defines a set of headers which can be exchanged between the client and the server which allow the server to relax the cross-domain restrictions for all HTTP verbs, not only GET. Also, since CORS is implemented in the same XmlHttpRequest as “normal” AJAX calls (in Firefox 3.5 and above, Safari 4 and above, Chrome 3 and above, IE 10 and above – in IE8/9, the code needs to use the XDomainRequest object instead), the JavaScript code doesn’t need to worry about “un-padding” responses or adding dummy functions. The error handling is also improved with CORS, since services can use the full range of the HTTP response codes (instead of 200, which is required by JSONP) and the code also has access to the full response instead of only its body.

CORS operation

There are two types of requests in the CORS world, “normal” requests and preflight requests. Normal requests are the requests which the page would normally make to the service, with an additional header, “Origin”, which indicates the origin and the service can determine whether to allow cross-domain calls from that origin or not (via the “Access-Control-Allow-Origin” response header). “Safe” requests (GET and HEAD) only use that extra headers to work. The browser will add the Origin header to requests going to domains other than the one where the page originated, and if the service doesn’t allow that domain, then the call will fail.

“Unsafe” requests, such as POST, PUT or DELETE, can’t be done the same way. If the service isn’t CORS-aware, it would ignore the “Origin” header and accept the request, with possible side effects (e.g., deleting a record), and at the time the client gets the response, the browser could still “fail” the request, but the damage has already been done. What the browser does in those cases is to first send a preflight request, which is a HTTP OPTIONS request asking for permission to send the actual request. If the service answers that request allowing the call, only then the browser will send the user request to the service.

CORS in WCF

So let’s start with the “normal” requests. That’s actually fairly simple to implement – we can use an inspector to check the “Origin” header in the requests, and if it’s present (and we want to allow the cross-domain request) on the reply we add the “Access-Control-Allow-Origin” header. As usual, we’ll start with a simple example, and go from there. And to make the comparison between WCF and the version I wrote in ASP.NET Web API easier, let’s use the exact same contract as that one.

  1. [ServiceContract]
  2. public interface IValues
  3. {
  4.     [WebGet(UriTemplate = "values", ResponseFormat = WebMessageFormat.Json)]
  5.     List<string> GetValues();
  6.     [WebGet(UriTemplate = "values/{id}", ResponseFormat = WebMessageFormat.Json)]
  7.     string GetValue(string id);
  8.     [WebInvoke(UriTemplate = "/values", Method = "POST", ResponseFormat = WebMessageFormat.Json)]
  9.     void AddValue(string value);
  10.     [WebInvoke(UriTemplate = "/values/{id}", Method = "DELETE", ResponseFormat = WebMessageFormat.Json)]
  11.     void DeleteValue(string id);
  12.     [WebInvoke(UriTemplate = "/values/{id}", Method = "PUT", ResponseFormat = WebMessageFormat.Json)]
  13.     string UpdateValue(string id, string value);
  14. }

The implementation is exactly the same as in the Web API one, so I’ll leave it out. Now, we need one “tagging” attribute to indicate whether an operation can be called via cross-domain calls or not. We can use an empty operation behavior attribute, which will be easily accessible via the operation description later.

  1. public class CorsEnabledAttribute : Attribute, IOperationBehavior
  2. {
  3.     public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
  4.     {
  5.     }
  6.  
  7.     public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
  8.     {
  9.     }
  10.  
  11.     public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
  12.     {
  13.     }
  14.  
  15.     public void Validate(OperationDescription operationDescription)
  16.     {
  17.     }
  18. }

And by having the CorsEnabled attribute as a IOperationBehavior, it allows us to filter through he operations for which we should implement the CORS handshake in our endpoint behavior.

  1. class EnableCorsEndpointBehavior : IEndpointBehavior
  2. {
  3.     public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
  4.     {
  5.     }
  6.  
  7.     public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
  8.     {
  9.     }
  10.  
  11.     public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
  12.     {
  13.         List<OperationDescription> corsEnabledOperations = endpoint.Contract.Operations
  14.             .Where(o => o.Behaviors.Find<CorsEnabledAttribute>() != null)
  15.             .ToList();
  16.         endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CorsEnabledMessageInspector(corsEnabledOperations));
  17.     }
  18.  
  19.     public void Validate(ServiceEndpoint endpoint)
  20.     {
  21.     }
  22. }

The inspector is divided in two parts: incoming requests and the verification whether the “Origin” header was sent and whether the operation for where the request is directed is one of those which are CORS-enabled. The first information we get via the HttpRequestMessageProperty property. The second one we could look at the request URI, but since the inspector is executed after the operation selector, that information is already available in the message properties via the WebHttpDispatchOperationSelector.HttpOperationNamePropertyName key. If those two conditions are met, then we return the value of the Origin header, which will be passed to the BeforeSendReply method of the inspector.

The second part, for the response, starts by looking at the correlation state returned by the AfterReceiveRequest method. If there is something, then the request had an Origin header, and we’ll use the HttpResponseMessageProperty on the reply message to send back the Access-Control-Allow-Origin method.

  1. class CorsEnabledMessageInspector : IDispatchMessageInspector
  2. {
  3.     private List<string> corsEnabledOperationNames;
  4.  
  5.     public CorsEnabledMessageInspector(List<OperationDescription> corsEnabledOperations)
  6.     {
  7.         this.corsEnabledOperationNames = corsEnabledOperations.Select(o => o.Name).ToList();
  8.     }
  9.  
  10.     public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
  11.     {
  12.         HttpRequestMessageProperty httpProp = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
  13.         object operationName;
  14.         request.Properties.TryGetValue(WebHttpDispatchOperationSelector.HttpOperationNamePropertyName, out operationName);
  15.         if (httpProp != null && operationName != null && this.corsEnabledOperationNames.Contains((string)operationName))
  16.         {
  17.             string origin = httpProp.Headers[CorsConstants.Origin];
  18.             if (origin != null)
  19.             {
  20.                 return origin;
  21.             }
  22.         }
  23.  
  24.         return null;
  25.     }
  26.  
  27.     public void BeforeSendReply(ref Message reply, object correlationState)
  28.     {
  29.         string origin = correlationState as string;
  30.         if (origin != null)
  31.         {
  32.             HttpResponseMessageProperty httpProp = null;
  33.             if (reply.Properties.ContainsKey(HttpResponseMessageProperty.Name))
  34.             {
  35.                 httpProp = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name];
  36.             }
  37.             else
  38.             {
  39.                 httpProp = new HttpResponseMessageProperty();
  40.                 reply.Properties.Add(HttpResponseMessageProperty.Name, httpProp);
  41.             }
  42.  
  43.             httpProp.Headers.Add(CorsConstants.AccessControlAllowOrigin, origin);
  44.         }
  45.     }
  46. }

And now, by decorating the GET operations with [CorsEnabled] and adding the EnableCorsEndpointBehavior to the endpoint, those operations can now be called via cross-domain.

Implementing preflight requests

The first part was easy. For the second part, we need to intercept the requests with the OPTIONS verb, and return the response immediately, without going to the operation. And that’s probably one of the biggest features missing in the WCF extensions – the ability to bypass the rest of the WCF pipeline at a given point. The first option is to use a custom reply channel (which, with anything in the channel layer, is really hard to write). The other option is to use a custom operation invoker which can bypass the actual operation. But the invoker by itself doesn’t work – on the invoker call you don’t have a reference to either the incoming request (to look for CORS headers) or the outgoing response (to set the response headers). Also, in order for the invoker to be called for OPTIONS requests (instead of the actual request), we needed to also have an operation selector which will map requests for OPTIONS verb to the actual operation. And it would also need some way to not map multiple operations which have the same URI template (but different verbs) to different operations, since OPTIONS will be the common ground there. And we’d also need to change the formatter so that the response for the OPTIONS request would be an empty response instead of the actual result of the operation… In short, not simple at all.

Another option, which I got the idea from the post about how to add dynamic operations from Zufilqar’s blog, is to not change the existing operations, but instead add new ones to handle the OPTIONS requests. Since I always try to avoid channels programming whenever possible, this seemed the best option given all the problems of using a custom invoker for the existing operations. Those operations actually need a custom invoker, but by making them operations with untyped messages (Message in, Message out), we don’t need a custom formatter, and we can access to the HTTP headers via the message properties as well.

To make this scenario simpler to use, let’s create a custom service host (and service host factory) to wrap the logic for creating the new operations. The service host will use as the contract type either the service type itself (if it is decorated with ServiceContractAttribute), or one interface the service type implements (and the interface is decorated with ServiceContractAttribute). More complex logic can be added if needed, but for this scenario, this is enough.

When the service host is being opened, we’ll add the single endpoint, find all the operations which are decorated with the CorsEnabled attribute, and for those, add a corresponding operation which deals with the preflight requests.

  1. class CorsEnabledServiceHostFactory : ServiceHostFactory
  2. {
  3.     protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
  4.     {
  5.         return new CorsEnabledServiceHost(serviceType, baseAddresses);
  6.     }
  7. }
  8.  
  9. class CorsEnabledServiceHost : ServiceHost
  10. {
  11.     Type contractType;
  12.     public CorsEnabledServiceHost(Type serviceType, Uri[] baseAddresses)
  13.         : base(serviceType, baseAddresses)
  14.     {
  15.         this.contractType = GetContractType(serviceType);
  16.     }
  17.  
  18.     protected override void OnOpening()
  19.     {
  20.         ServiceEndpoint endpoint = this.AddServiceEndpoint(this.contractType, new WebHttpBinding(), "");
  21.  
  22.         List<OperationDescription> corsEnabledOperations = endpoint.Contract.Operations
  23.             .Where(o => o.Behaviors.Find<CorsEnabledAttribute>() != null)
  24.             .ToList();
  25.  
  26.         AddPreflightOperationSelectors(endpoint, corsEnabledOperations);
  27.  
  28.         endpoint.Behaviors.Add(new WebHttpBehavior());
  29.         endpoint.Behaviors.Add(new EnableCorsEndpointBehavior());
  30.  
  31.         base.OnOpening();
  32.     }
  33.  
  34.     private Type GetContractType(Type serviceType)
  35.     {
  36.         if (HasServiceContract(serviceType))
  37.         {
  38.             return serviceType;
  39.         }
  40.  
  41.         Type[] possibleContractTypes = serviceType.GetInterfaces()
  42.             .Where(i => HasServiceContract(i))
  43.             .ToArray();
  44.  
  45.         switch (possibleContractTypes.Length)
  46.         {
  47.             case 0:
  48.                 throw new InvalidOperationException("Service type " + serviceType.FullName + " does not implement any interface decorated with the ServiceContractAttribute.");
  49.             case 1:
  50.                 return possibleContractTypes[0];
  51.             default:
  52.                 throw new InvalidOperationException("Service type " + serviceType.FullName + " implements multiple interfaces decorated with the ServiceContractAttribute, not supported by this factory.");
  53.         }
  54.     }
  55.  
  56.     private static bool HasServiceContract(Type type)
  57.     {
  58.         return Attribute.IsDefined(type, typeof(ServiceContractAttribute), false);
  59.     }
  60. }

In order to add the preflight operations, we first iterate over all the CORS-enabled operations which need to respond to the preflight request (GET requests don’t need those). For those operations, we first get the URI template for the operation, and normalize it (remove query string parameters, and remove the parameter lists replacing them with wildcards) so that two operations with similar URI templates (e.g., [WebInvoke(Method = “POST”, UriTemplate = “/products/{param1}?x={param2}”)] and [WebInvoke(Method = “DELETE”, UriTemplate = “/products/{id}”)]) will have only one new operation for the “/products/*” URI. If there is already an OPTIONS operation for the normalized URI we’ll add HTTP verb to it, otherwise we’ll create a new operation to handle the OPTIONS request.

  1. private void AddPreflightOperations(ServiceEndpoint endpoint, List<OperationDescription> corsOperations)
  2. {
  3.     Dictionary<string, PreflightOperationBehavior> uriTemplates = new Dictionary<string, PreflightOperationBehavior>(StringComparer.OrdinalIgnoreCase);
  4.  
  5.     foreach (var operation in corsOperations)
  6.     {
  7.         if (operation.Behaviors.Find<WebGetAttribute>() != null || operation.IsOneWay)
  8.         {
  9.             // no need to add preflight operation for GET requests, no support for 1-way messages
  10.             continue;
  11.         }
  12.  
  13.         string originalUriTemplate;
  14.         WebInvokeAttribute originalWia = operation.Behaviors.Find<WebInvokeAttribute>();
  15.  
  16.         if (originalWia != null && originalWia.UriTemplate != null)
  17.         {
  18.             originalUriTemplate = NormalizeTemplate(originalWia.UriTemplate);
  19.         }
  20.         else
  21.         {
  22.             originalUriTemplate = operation.Name;
  23.         }
  24.  
  25.         string originalMethod = originalWia != null && originalWia.Method != null ? originalWia.Method : "POST";
  26.  
  27.         if (uriTemplates.ContainsKey(originalUriTemplate))
  28.         {
  29.             // there is already an OPTIONS operation for this URI, we can reuse it
  30.             PreflightOperationBehavior operationBehavior = uriTemplates[originalUriTemplate];
  31.             operationBehavior.AddAllowedMethod(originalMethod);
  32.         }
  33.         else
  34.         {
  35.             ContractDescription contract = operation.DeclaringContract;
  36.             OperationDescription preflightOperation;
  37.             PreflightOperationBehavior preflightOperationBehavior;
  38.             CreatePreflightOperation(operation, originalUriTemplate, originalMethod, contract, out preflightOperation, out preflightOperationBehavior);
  39.             uriTemplates.Add(originalUriTemplate, preflightOperationBehavior);
  40.  
  41.             contract.Operations.Add(preflightOperation);
  42.         }
  43.     }
  44. }

Creating the preflight operation means creating a new operation description for the contract, adding two messages to it: an input message with a single body part of type Message, and an output message with a return value of the same type. We then use the same URI template as the original operation, and add a WebInvokeAttribute to the operation. We then add a DataContractSerializerOperationBehavior to the operation description, since it will give us a formatter which understands the (Message in, Message out) pattern. Finally, we add our custom operation behavior, which we’ll use to implement the operation invoker which will ultimately deal with the preflight request

  1. private static void CreatePreflightOperation(OperationDescription operation, string originalUriTemplate, string originalMethod, ContractDescription contract, out OperationDescription preflightOperation, out PreflightOperationBehavior preflightOperationBehavior)
  2. {
  3.     preflightOperation = new OperationDescription(operation.Name + CorsConstants.PreflightSuffix, contract);
  4.     MessageDescription inputMessage = new MessageDescription(operation.Messages[0].Action + CorsConstants.PreflightSuffix, MessageDirection.Input);
  5.     inputMessage.Body.Parts.Add(new MessagePartDescription("input", contract.Namespace) { Index = 0, Type = typeof(Message) });
  6.     preflightOperation.Messages.Add(inputMessage);
  7.     MessageDescription outputMessage = new MessageDescription(operation.Messages[1].Action + CorsConstants.PreflightSuffix, MessageDirection.Output);
  8.     outputMessage.Body.ReturnValue = new MessagePartDescription(preflightOperation.Name + "Return", contract.Namespace) { Type = typeof(Message) };
  9.     preflightOperation.Messages.Add(outputMessage);
  10.  
  11.     WebInvokeAttribute wia = new WebInvokeAttribute();
  12.     wia.UriTemplate = originalUriTemplate;
  13.     wia.Method = "OPTIONS";
  14.  
  15.     preflightOperation.Behaviors.Add(wia);
  16.     preflightOperation.Behaviors.Add(new DataContractSerializerOperationBehavior(preflightOperation));
  17.     preflightOperationBehavior = new PreflightOperationBehavior(preflightOperation);
  18.     preflightOperationBehavior.AddAllowedMethod(originalMethod);
  19.     preflightOperation.Behaviors.Add(preflightOperationBehavior);
  20. }

Finally, the custom invoker. The implementation of the IOperationInvoker interface is fairly trivial: allocate 1 input, only work with synchronous operations. The Invoke calls the operation to handle the preflight request: take the incoming message HttpRequestMessageProperty property, get any CORS-specific headers, then create an empty reply message, and add to it a HttpResponseMessageProperty property with the appropriate headers.

  1. class PreflightOperationInvoker : IOperationInvoker
  2. {
  3.     private string replyAction;
  4.     List<string> allowedHttpMethods;
  5.     
  6.     public PreflightOperationInvoker(string replyAction, List<string> allowedHttpMethods)
  7.     {
  8.         this.replyAction = replyAction;
  9.         this.allowedHttpMethods = allowedHttpMethods;
  10.     }
  11.  
  12.     public object[] AllocateInputs()
  13.     {
  14.         return new object[1];
  15.     }
  16.  
  17.     public object Invoke(object instance, object[] inputs, out object[] outputs)
  18.     {
  19.         Message input = (Message)inputs[0];
  20.         outputs = null;
  21.         return HandlePreflight(input);
  22.     }
  23.  
  24.     public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
  25.     {
  26.         throw new NotSupportedException("Only synchronous invocation");
  27.     }
  28.  
  29.     public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
  30.     {
  31.         throw new NotSupportedException("Only synchronous invocation");
  32.     }
  33.  
  34.     public bool IsSynchronous
  35.     {
  36.         get { return true; }
  37.     }
  38.  
  39.     Message HandlePreflight(Message input)
  40.     {
  41.         HttpRequestMessageProperty httpRequest = (HttpRequestMessageProperty)input.Properties[HttpRequestMessageProperty.Name];
  42.         string origin = httpRequest.Headers[CorsConstants.Origin];
  43.         string requestMethod = httpRequest.Headers[CorsConstants.AccessControlRequestMethod];
  44.         string requestHeaders = httpRequest.Headers[CorsConstants.AccessControlRequestHeaders];
  45.  
  46.         Message reply = Message.CreateMessage(MessageVersion.None, replyAction);
  47.         HttpResponseMessageProperty httpResponse = new HttpResponseMessageProperty();
  48.         reply.Properties.Add(HttpResponseMessageProperty.Name, httpResponse);
  49.  
  50.         httpResponse.SuppressEntityBody = true;
  51.         httpResponse.StatusCode = HttpStatusCode.OK;
  52.         if (origin != null)
  53.         {
  54.             httpResponse.Headers.Add(CorsConstants.AccessControlAllowOrigin, origin);
  55.         }
  56.  
  57.         if (requestMethod != null && this.allowedHttpMethods.Contains(requestMethod))
  58.         {
  59.             httpResponse.Headers.Add(CorsConstants.AccessControlAllowMethods, string.Join(",", this.allowedHttpMethods));
  60.         }
  61.  
  62.         if (requestHeaders != null)
  63.         {
  64.             httpResponse.Headers.Add(CorsConstants.AccessControlAllowHeaders, requestHeaders);
  65.         }
  66.  
  67.         return reply;
  68.     }
  69. }

That’s it. We can now use the custom service host factory to webhost our service, and use a page in another web service to call our service – as long as the browser supports CORS (which means the latest Chrome and Firefox, and IE 10 and above). You can find the code for a sample page at the sample in the code gallery.

Final thoughts: WCF vs. ASP.NET Web APIs

This is a good example to compare the extensibility between WCF and the ASP.NET Web APIs. The implementation of this scenario took a lot more code (and a non-negligible number of extension points) to be done in WCF compared with a similar solution in Web API. This is a specific HTTP scenario, and for those cases, Web APIs will likely be easier. But what I tried to do (and did) was to show that it can be done in WCF, so if you have an investment in that technology, you don’t need to hurry to make the change (if you’re starting a new project, and the focus of the project is HTTP only and the Web, then Web API would be a logical choice).

[Code in this post]

Comments

  • Anonymous
    May 14, 2012
    Interesting Finds: May 15, 2012
  • Anonymous
    June 13, 2012
    Hi,Thanks for this, it explains a lot.However, after trying to implement it I found this exception in the logs:"A binding instance has already been associated to listen URI 'http://<mysite>/jsapi/Login/1.0.0/Auth.svc'. If two endpoints want to share the same ListenUri, they must also share the same binding object instance. The two conflicting endpoints were either specified in AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint() and config."The web.config has the following: <system.serviceModel>     <services>         <service name="API.Login.Auth">             <endpoint name="apiEndPoint" contract="API.Login.IAuth" address="" binding="webHttpBinding" behaviorConfiguration="webHttpBehavior" />         </service>parent web.config has:       <behaviors>           <endpointBehaviors>               <behavior name="webHttpBehavior">                   <webHttp defaultBodyStyle="Bare" defaultOutgoingResponseFormat="Json" automaticFormatSelectionEnabled="false" />               </behavior>           </endpointBehaviors>       </behaviors>Is there some kind of double association of binding objects going on ? The error message seems to indicate the CORS support code above is adding something that I had already added and that the config needs to be adjusted accordingly.Any thoughts on what that config adjustment would need to be ? Thanks.
  • Anonymous
    June 14, 2012
    Rick, if you're defining endpoints using a service host factory (as I did on the example above), you shouldn't define them via config as well, otherwise you may end up with two endpoints trying to listen to the same address.
  • Anonymous
    June 19, 2012
    Does this work when the WCF service is https?
  • Anonymous
    July 15, 2012
    The comment has been removed
  • Anonymous
    September 11, 2012
    This works well with IE but it does not work in Firefox and Chrome. I have specified the headers for handling options for FF and Chrome in global.asax file.HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "http://localhost:16154");           if (HttpContext.Current.Request.HttpMethod == "OPTIONS")           {               //These headers are handling the "pre-flight" OPTIONS call sent by the browser               HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");               HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Origin, Content-Type, Accept");               HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");               HttpContext.Current.Response.End();           }Fiddler shows that the response is going through without any problem but Chrome and FF says that access is denied because of the same origin policy.Please advise...Thanks!!!
  • Anonymous
    September 20, 2012
    @PrashantI had the same issue, I got round it by sending CORS headers back when return data and not just when the OPTIONS request is received. It's not pretty but I haven't had time to make it more elegant, it works with Firefox and Chrome using a Javascript mobile client.public List<dtCustomer> GetCustomers(string SearchTerm)       {           try           {               // Check for browser pre-flight CORS request               if (WebOperationContext.Current.IncomingRequest.Method == "OPTIONS")               {                   AllowAccess();                   return null;               }               // Stop an empty search returning everything, this is enforced on the mobile client but for other services services that connect               if (SearchTerm == null)               {                   AllowAccess();                   return null;               }               using (DataAccessAdapter adapter = new DataAccessAdapter())               {                   LinqMetaData metaData = new LinqMetaData(adapter);                   var q = metaData.Customer.Where(f => f.Surname.StartsWith(SearchTerm))                           .Select(dCustomers => new dtCustomerList                               {                                   Forename = dCustomers.Forename,                                   Surname = dCustomers.Surname,                                   DOB = dCustomers.DOB                               });                   AllowAccess();                   return q.ToList() ;               }           }           catch (Exception ex)           {               throw (ex);           }       }       /// <summary>       /// Set response header for browser CORS support       /// </summary>       /// <returns>       /// </returns>       private void AllowAccess()       {           WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "http://Server:Port"); // * doesn't work           WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "GET");           WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "POST");           WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept");       }   }Service contract       [OperationContract]       [WebInvoke(Method = "*",                 RequestFormat = WebMessageFormat.Json,                 UriTemplate = "/GetCustomers",                 ResponseFormat = WebMessageFormat.Json)]       List<dtCustomer> GetCustomers(string SearchTerm);   [DataContract]   public class dtCustomer   {       [DataMember]       public string Forename { get; set; }       [DataMember]       public string Surname { get; set; }       [DataMember]       public string POST { get; set; }   }This works with POST.Hope this is of some use.
  • Anonymous
    October 15, 2012
    Hi, I have implemented CORS successfully. But now, I need to pass a lot of data from the Service to Client Side. I know we can set <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />.But I do not know where to set these values. Do I have to do it web.config like we always do or in any of the cs files that I downloaded to make CORS work.Please advise...Thanks!
  • Anonymous
    October 16, 2012
    The comment has been removed
  • Anonymous
    January 20, 2013
    Carlos -excellent article and thank you - I have implemented CORS successfully, but have have also implemented an authorizationmanager that inherits from ServiceAuthorizationManager. The function CheckAccessCore is hit and if the user is not authorised I would like to send a meaningful message back to the browser. What happens is that because I'm returning false from the checkaccesscore function...the CorsEnabledMessageInspector - "AfterReceiveRequest" method does not get hit and thus the browser receives the "Origin http://localhost:51106 is not allowed by Access-Control-Allow-Origin" message. - Any advice as to how to workaround this?Thanks in advance!
  • Anonymous
    October 08, 2013
    I am trying to acess https wcf web service hosted on different domain.I am getting following error:The HTTP method 'OPTIONS' of the incoming request (with URI 'https://serverName/Secure/Service.svc/GetName') is not allowed.Here is config details :<system.serviceModel>   <bindings>     <wsHttpBinding>       <binding name="wsHttpBindingClient">         <security mode="Transport"/>       </binding>     </wsHttpBinding>     <webHttpBinding>       <binding name="webHttpBindingClient" closeTimeout="00:50:00" openTimeout="00:50:00" receiveTimeout="00:50:50" sendTimeout="00:50:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" transferMode="Buffered" useDefaultWebProxy="true" crossDomainScriptAccessEnabled="true">         <security mode="Transport"/>       </binding>     </webHttpBinding>   </bindings>   <services>     <service behaviorConfiguration="ServiceBehaviour" name="Service">       <endpoint address="" behaviorConfiguration="EndpBehaviour" binding="webHttpBinding" bindingConfiguration="webHttpBindingClient" name="webHttpEndPointBinding" contract="IService"/>     </service>   </services>   <behaviors>     <serviceBehaviors>       <behavior name="ServiceBehaviour">                 <serviceMetadata httpsGetEnabled="true"/>                 <serviceDebug includeExceptionDetailInFaults="true"/>       </behavior>     </serviceBehaviors>     <endpointBehaviors>       <behavior name="EndpBehaviour">         <webHttp helpEnabled="true"/>       </behavior>     </endpointBehaviors>   </behaviors>   <service HostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>   <standardEndpoints>     <webHttpEndpoint>       <standardEndpoint crossDomainScriptAccessEnabled="true"/>     </webHttpEndpoint>     <webScriptEndpoint>       <standardEndpoint crossDomainScriptAccessEnabled="true"/>     </webScriptEndpoint>   </standardEndpoints> </system.serviceModel>Please advise....Thanks
  • Anonymous
    October 22, 2013
    Thank you for your contributions Carlos,How would one need to modify this implementation if the REST service is hosted in a Windows Service?Thanks
  • Anonymous
    June 10, 2014
    Hi,I have download your code and run. I have called the method from JavaScript like:var valuesAddress = "http://localhost:8080/Values";       $("#getAll").click(function () {           $.ajax({               url: valuesAddress,               type: "GET",               success: function (result) {                   var text = "";                   for (var i = 0; i < result.length; i++) {                       if (i > 0) text = text + ", ";                       text = text + result[i];                   }                   $("#result").text(text);               },               error: function (jqXHR, textStatus, errorThrown) {                   $("#result").text(textStatus);               }           });       });this run successfully but when I change the address like:var valuesAddress = "192.168.1.103/Values"; (IP Address instead of localhost)its retuning error and got following message:"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://192.168.1.103:85/Values. This can be fixed by moving the resource to the same domain or enabling CORS" !!!What is the resolution?Thanks,Jahid
  • Anonymous
    June 17, 2014
    Carlos, thank you very much for this blog post! It was very helpful - for the first time I actually understand what CORS is supposed to be doing.
  • Anonymous
    July 13, 2014
    how to configure self hosted WCF with CORS?
  • Anonymous
    July 17, 2014
    To use it in a self-hosted project, you can just use the CorsEnabledServiceHost type and open it directly.
  • Anonymous
    November 18, 2014
    Perfect code! Thanks.
  • Anonymous
    January 11, 2015
    Hello @CarlosFigueira,&nbsp;                                   I tried the code provided by you but it's not working in Chrome and FF at all.I tried solution provided by @Taz  also but it's not working,I am using visual studio 2012.
  • Anonymous
    February 18, 2015
    CarlosFigueira, I have been struggling with getting CORS enabled for a WCF Service. I want to know, if WebInvoke attribute is mandatory to be added OperationContract in the WCF Service? Can I get it working without it? Since, if I host the client and server on the same server, I could call the WCF Service in SOAP call from javascript. Of course, I build the SOAP xml but it works. Please help!!
  • Anonymous
    July 13, 2015
    When I use this in a cross domain configuration, the GET work fine, returns data but the POST fails with bad request. What am I doing wrong?