The REST APIs are designed to work with Web i.e. a network of different connected domain that works together by sharing resources. But in today’s world we have all modern browsers which are highly supporting the CORS. Those who don’t know about CORS can learn about it here.
In a simple language, CORS is simply allowing other domain to request for a resource hosted in a another domain. So, for example, if an image is hosted on SiteA.com and SiteB.com wants to access this image to display in one of his page, then SiteA.com has to allow it. The browser which is accessing SiteB.com will know by reading the response header which are coming after making a request to SiteA.com. So if it has CORS header information then the request can be fetched otherwise browser will simply reject the response without displaying any specific error.
Sample Response headers of CORS from SiteA.com:
- Access-Control-Allow-Origin: http://SiteB.example
- Access-Control-Allow-Methods: POST, GET, OPTIONS
- Access-Control-Allow-Headers: X-PINGOTHER
- Access-Control-Max-Age: 1728000
You visualize the above situation something like below though it’s not a complete coverage of whole CORS concept but it will get you going.
Know more about the Server side CORS and Client CORS. The same scenario applies when a cross domain REST API request is made. So your WCF REST API service should serve the response with CORS headers. The feature is not supported out of box but WCF provides a lot of extensibility points. So you can extend/create a new behavior and plug it in to have your WCF REST API CORS enabled.
So conceptually you can develop a new Endpoint behavior Endpoint behavior with a custom message inspector message inspector that will inject the CORS headers settings in the response your requesting browser doesn’t deny it.
Here’s how the custom message inspector would look like the following:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.ServiceModel.Channels;
- using System.ServiceModel.Dispatcher;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace WebHttpBehaviorExtensions.Cors
- {
- public class CustomHeaderMessageInspector : IDispatchMessageInspector
- {
- Dictionary<string, string> requiredHeaders;
- public CustomHeaderMessageInspector(Dictionary<string, string> headers)
- {
- requiredHeaders = headers ?? new Dictionary<string, string>();
- }
-
- public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
- {
- return null;
- }
-
- public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
- {
- var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
- foreach (var item in requiredHeaders)
- {
- httpHeader.Headers.Add(item.Key, item.Value);
- }
- }
- }
- }
Message inspector is actually that adds the headers into response with method
BeforeSendReply. Now we have to inject this custom message inspector object into collection of default message inspector. To do that we have created a new class implementing
IEndpointBehavior interface, also we will be using the same class to extend the base class
BehaviorExtensionElement (This would help injecting the behavior from configuration.).
- using System;
- using System.Collections.Generic;
- using System.ServiceModel.Configuration;
- using System.ServiceModel.Description;
-
- namespace WebHttpBehaviorExtensions.Cors
- {
- class EnableCorsBehavior : BehaviorExtensionElement, IEndpointBehavior
- {
- public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
- { }
-
- public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
- { }
-
- public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
- {
- var requiredHeaders = new Dictionary<string, string>();
-
- requiredHeaders.Add("Access-Control-Allow-Origin", "*");
- requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
- requiredHeaders.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
-
- endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomHeaderMessageInspector(requiredHeaders));
- }
-
- public void Validate(ServiceEndpoint endpoint) { }
-
- public override Type BehaviorType
- {
- get { return typeof(EnableCorsBehavior); }
- }
-
- protected override object CreateBehavior()
- {
- return new EnableCorsBehavior();
- }
- }
- }
That’s it! Now to show you the configuration settings I’ll just put the parts which are needed to be updated with this new behavior setting. In configuration under
<service.model> add the following endpoint behavior:
Define the Extension behavior first:
- <extensions>
- <behaviorExtensions>
- <add name="corsBehavior" type="WebHttpBehaviorExtensions.Cors.EnableCorsBehavior, WebHttpBehaviorExtensions, Version=1.0.0.0, Culture=neutral" />
- </behaviorExtensions>
- </extensions>
Add behavior
- <behaviors>
- <endpointBehaviors>
- <behavior name="webHttpServiceBehavior">
- <corsBehavior />
- <webHttp/>
- </behavior>
- </endpointBehaviors>
- </behaviors>
Now add the custom behavior settings to your endpoint in <services> section:
- <services>
- <service name="WcfWebHttpIISHostingSample.TestService" behaviorConfiguration="ServiceBehavior">
- <endpoint binding="webHttpBinding" contract="WcfWebHttpIISHostingSample.ITestService" behaviorConfiguration="webHttpServiceBehavior" />
- </service>
- </services>
That’s it! Now you can test your settings by deploying the WCF REST service to IIS and when making a request press F12 and look for Network request and response Headers. You can also use tools like
`Fiddler` and `Chrome Post Master` to perform a Get/Post request and analyzing header.
This blog post is part of an
Extension behavior library that I’m currently working on to provide a set of Regularly required feature which aren’t available out of the box. To download the Library via Nuget from
here:
If you like to contribute or want to look at the source code, the complete
source code is open source and hosted on
Github.