Overview
Cross-origin resource sharing (CORS) is a standard that allows web pages to make AJAX requests to another domain. It relaxes the same-origin policy implemented on the web browsers that limits the calls to be within the same domain.
The CORS spec (http://www.w3.org/TR/cors/) defines the way the server and browser interact in order to make cross origin calls (that is, cross domain). Most of the modern browsers today already support CORS. Our goal is to enable the support for our Web API services.
Required Assemblies
System.Web.Cors.dll
This assembly contains the core CORS library and has no dependency on System.Web.dll or System.Web.Http.dll.
System.Web.Http.Cors.dll
This assembly contains the library for enabling CORS on Web API and has dependency on System.Web.Cors.dll and System.Web.Http.dll.
Scenarios
Enabling CORS
We’ve added a new extension method to the HttpConfiguration to enable CORS. With this, you can enable the support globally, per controller or per action.
Globally
You can define a global setting when calling EnableCors. For example, the following will enable CORS globally, allowing all origins, methods, and headers. There are many settings on the EnableCorsAttribute that you can configure and are shown later in this document.
Per Controller
The support can also be scoped to the controller. First you just need to call EnableCors without providing a global setting (that is, (new EnableCorsAttribute()).
Then you can declare the EnableCorsAttribute on the controller to enable CORS.
Per Action
In a similar fashion, you can enable CORS on a single action by first calling EnableCors.
And then declare the EnableCorsAttribute on an action.
Attribute precedence
When you have the EnableCorsAttribute applied on all scopes (globally, per-controller, per-action), the closest one to the resource wins. Therefore the precedence is defined as follows:
- Action
- Controller
- Global
Excluding a controller or an action from EnableCors
You can use [DisableCors] attribute to exclude a controller or and action from the global or per-controller settings. For example, the following will enable CORS for all the actions in the ValuesController except for Get(int id).
Configuring [EnableCors] attribute
There’re few settings under the EnableCorsAttribute. These settings are defined by the CORS spec (http://www.w3.org/TR/cors/#resource-processing-model).
- Origins
- Headers
- Methods
- ExposedHeaders
- SupportsCredentials
- PreflightMaxAge
By default, EnableCorsAttribute will allow all origins, methods and headers. Note that when you declare the attribute on an action it automatically assumes the HTTP Method of the action that you declared on.
As soon as you specify the origins, you are basically limiting the access to the specified origins. The same applies to the methods and the headers.
For example, the following will only allow “http://localhost” and “http://sample.com” to access the ValuesController from the browser though AJAX. Note that it is still allowing any methods and headers because they’re not specified.
Implementing a custom ICorsPolicyProvider
You can implement ICorsPolicyProvider to load the CORS settings/policy dynamically from other sources such as the web.config file or a database. In fact, both the EnableCorsAttribute and DisableCorsAttribute implement this interface internally.
Note that the ICorsPolicyProvider is async so that we don’t block the thread on I/O.
Sample
Here is a custom implementation of ICorsPolicyProvider that loads the origins from web.config.
You can apply it on the controller/action just like EnableCorsAttribute.
And it will read the “internal:origins” appSetting from the web.config.
Implementing a custom ICorsPolicyProviderFactory
ICorsPolicyProviderFactory is an abstraction that allows you to specify how the ICorsPolicyProvider is retrieved. By default we provide the AttributeBasedPolicyProviderFactory which allows you to specify the ICorsPolicyProvider as attributes ([EnableCors], [DisableCors]). However you can extend theICorsPolicyProviderFactory to create a centralized configuration model.
You can register the custom ICorsPolicyProviderFactory through SetCorsPolicyProviderFactory extension method.
Sample
Here is a custom implementation of ICorsPolicyProviderFactory that allows you to configure the CORS settings through your own CorsConfiguration class instead of attributes.
Once the ConfigBasedPolicyProviderFactory is registered, it will enable CORS on ValuesController and UsersController.
Integration with Web API Tracing
When you call config.EnableCors(), it automatically adds the necessary tracers when the ITraceWriter is provided.
using System.Web.Http.Cors; public static class WebApiConfig { public static void Register(HttpConfiguration config) { // other settings removed for clarity config.EnableSystemDiagnosticsTracing(); config.EnableCors(); } }
It will emit traces similar to what’s highlighted below when you have the Web API tracing package installed and enabled.
iisexpress.exe Information: 0 : Request, Method=GET, Url=http://localhost:33150/api/Values, Message='http://localhost:33150/api/Values'
iisexpress.exe Information: 0 : Message='Will use same 'JsonMediaTypeFormatter' formatter', Operation=JsonMediaTypeFormatter.GetPerRequestFormatterInstance
iisexpress.exe Information: 0 : Message='Will use same 'XmlMediaTypeFormatter' formatter', Operation=XmlMediaTypeFormatter.GetPerRequestFormatterInstance
iisexpress.exe Information: 0 : Message='Will use same 'FormUrlEncodedMediaTypeFormatter' formatter', Operation=FormUrlEncodedMediaTypeFormatter.GetPerRequestFormatterInstance
iisexpress.exe Information: 0 : Message='Will use same 'JQueryMvcFormUrlEncodedFormatter' formatter', Operation=JQueryMvcFormUrlEncodedFormatter.GetPerRequestFormatterInstance
iisexpress.exe Information: 0 : Message='Values', Operation=DefaultHttpControllerSelector.SelectController
iisexpress.exe Information: 0 : Message='CorsSample.Controllers.ValuesController', Operation=DefaultHttpControllerActivator.Create
iisexpress.exe Information: 0 : Message='CorsSample.Controllers.ValuesController', Operation=HttpControllerDescriptor.CreateController
iisexpress.exe Information: 0 : Message='Selected action 'Get()'', Operation=ApiControllerActionSelector.SelectAction
iisexpress.exe Information: 0 : Operation=HttpActionBinding.ExecuteBindingAsync
iisexpress.exe Information: 0 : Operation=QueryableAttribute.ActionExecuting
iisexpress.exe Information: 0 : Message='Action returned 'System.String[]'', Operation=ReflectedHttpActionDescriptor.ExecuteAsync
iisexpress.exe Information: 0 : Message='Will use same 'JsonMediaTypeFormatter' formatter', Operation=JsonMediaTypeFormatter.GetPerRequestFormatterInstance
iisexpress.exe Information: 0 : Message='Selected formatter='JsonMediaTypeFormatter', content-type='application/json; charset=utf-8'', Operation=DefaultContentNegotiator.Negotiate
iisexpress.exe Information: 0 : Operation=ApiControllerActionInvoker.InvokeActionAsync, Status=200 (OK)
iisexpress.exe Information: 0 : Operation=QueryableAttribute.ActionExecuted, Status=200 (OK)
iisexpress.exe Information: 0 : Operation=ValuesController.ExecuteAsync, Status=200 (OK)
iisexpress.exe Information: 0 : Response, Status=200 (OK), Method=GET, Url=http://localhost:33150/api/Values, Message='Content-type='application/json; charset=utf-8', content-length=unknown'
iisexpress.exe Information: 0 : Operation=CorsMessageHandler.SendAsync, Status=200 (OK)
iisexpress.exe Information: 0 : Message='CorsPolicyProvider selected: 'System.Web.Http.Cors.EnableCorsAttribute'', Operation=ConfigBasedPolicyProviderFactory.GetCorsPolicyProvider
iisexpress.exe Information: 0 : Message='CorsPolicy selected: 'AllowAnyHeader: True, AllowAnyMethod: True, AllowAnyOrigin: True, PreflightMaxAge: null, SupportsCredentials: False, Origins: {}, Methods: {}, Headers: {}, ExposedHeaders: {}'', Operation=EnableCorsAttribute.GetCorsPolicyAsync
iisexpress.exe Information: 0 : Message='CorsResult returned: 'IsValid: True, AllowCredentials: False, PreflightMaxAge: null, AllowOrigin: *, AllowExposedHeaders: {}, AllowHeaders: {}, AllowMethods: {}, ErrorMessages: {}'', Operation=CorsEngine.EvaluatePolicyiisexpress.exe Information: 0 : Operation=JsonMediaTypeFormatter.WriteToStreamAsync
iisexpress.exe Information: 0 : Operation=ValuesController.Dispose
No comments:
Post a Comment