Skip to content

Instantly share code, notes, and snippets.

@dereklawless
Last active January 10, 2020 11:45
Show Gist options
  • Save dereklawless/5849425 to your computer and use it in GitHub Desktop.
Save dereklawless/5849425 to your computer and use it in GitHub Desktop.
CORS support for ASP.NET Web API.
/// <summary>
/// An attribute to allow a CORS preflight request to a decorated action, returning an HTTP 200 OK status code.
/// </summary>
/// <remarks>See <see href="http://www.html5rocks.com/en/tutorials/cors/"/> for more details.</remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class AllowCorsPreflightAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var headers = actionContext.Request.Headers;
// Determine whether this is a CORS request.
if (headers.Contains(CorsHeaders.Origin))
{
if (actionContext.Request.Method == HttpMethod.Options)
{
if (headers.Contains(CorsHeaders.AccessControlRequestMethod))
{
// Determine whether the requested HTTP method is supported by the action.
var requestedHttpMethod = headers.GetValues(CorsHeaders.AccessControlRequestMethod).FirstOrDefault();
if (actionContext.ActionDescriptor.SupportedHttpMethods.Any(m => m.Method == requestedHttpMethod))
{
// Allow the request, additional CORS headers may be defined elsewhere.
actionContext.Response = new HttpResponseMessage(HttpStatusCode.OK);
}
}
}
}
}
}
/// <summary>
/// A custom controller configuration attribute, for wiring up CORS-aware action selectors.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class CorsControllerConfigurationAttribute : Attribute, IControllerConfiguration
{
/// <summary>
/// Gets or sets the action selector type.
/// </summary>
/// <value>The action selector type.</value>
public Type ActionSelector { get; set; }
public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
if (controllerSettings == null)
{
throw new ArgumentNullException("controllerSettings");
}
if (controllerDescriptor == null)
{
throw new ArgumentNullException("controllerDescriptor");
}
controllerSettings.Services.Replace(typeof(IHttpActionSelector), Activator.CreateInstance(ActionSelector));
}
}
/// <summary>
/// A custom controller action selector, for CORS preflight (HTTP OPTIONS) requests.
/// </summary>
/// <remarks>See <see href="http://www.html5rocks.com/en/tutorials/cors/"/> for more details.</remarks>
public class CorsPreflightActionSelector : ApiControllerActionSelector
{
public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
var originalRequest = controllerContext.Request;
var isCorsRequest = originalRequest.Headers.Contains(CorsHeaders.Origin);
if (originalRequest.Method == HttpMethod.Options && isCorsRequest)
{
// Get the 'Access-Control-Request-Method' HTTP method value sent in the request.
var accessControlRequestMethod = originalRequest.Headers.GetValues(CorsHeaders.AccessControlRequestMethod)
.FirstOrDefault();
if (!string.IsNullOrEmpty(accessControlRequestMethod))
{
// Create a new request for the HTTP method and URI specified in the preflight request.
var modifiedRequest = new HttpRequestMessage(new HttpMethod(accessControlRequestMethod), originalRequest.RequestUri);
controllerContext.Request = modifiedRequest;
// Select the action specified in the new request.
var actualDescriptor = base.SelectAction(controllerContext);
controllerContext.Request = originalRequest;
if (actualDescriptor != null)
{
// The action requested exists, determine whether this CORS request is allowed.
if (actualDescriptor.GetFilters().OfType<AllowCorsPreflightAttribute>().Any())
{
return actualDescriptor;
}
}
}
}
return base.SelectAction(controllerContext);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment