How to solve ASP.NET Web API CORS Preflight problem when using PUT and DELETE queries with multiple sources?

I have an ASP.NET web API that is called by three different SPAs. I am using Windows authentication for the web API. First I tried to configure CORS in Web.config as follows:

<httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="http://localhost:63342" /> <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE" /> <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" /> <add name="Access-Control-Allow-Credentials" value="true" /> </customHeaders> </httpProtocol> 

This caused a problem before flying:

 Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin (...) is therefore not allowed access. 

which I solved by adding the following method to Global.asax.cs:

 protected void Application_BeginRequest() { if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS") { Response.Flush(); } } 

This approach worked great for a single spa. I thought I could go to Web.config and add another origin as follows:

 <add name="Access-Control-Allow-Origin" value="http://localhost:63342,http://localhost:63347,http://localhost:63345/> 

but apparently this is unacceptable. This caused the following error:

 The 'Access-Control-Allow-Origin' header contains multiple values (...), but only one is allowed. Origin (...) is therefore not allowed access. 

So, to fix this, I changed my approach and instead decided to try setting CORS to WebAPIConfig.cs in the Register method, for example:

 var cors = new EnableCorsAttribute("http://localhost:63342,http://localhost:63347,http://localhost:63345", "Origin, X-Requested-With, Content-Type, Accept", "GET, POST, PUT, DELETE"); cors.SupportsCredentials = true; config.EnableCors(cors); 

I thought this would work, but now I have an error before flying when using PUT and DELETE requests, and I don’t know how to fix it. I debugged the Application_BeginRequest method and it still clears the OPTIONS request, so I don't know what causes this error. Does anyone know how I can solve this problem?

EDIT:

Print preflight error:

enter image description here

+6
source share
2 answers

I was able to solve my problem by setting the Application_BeginRequest method in the Global.asax.cs file as follows:

 protected void Application_BeginRequest() { if (Request.HttpMethod == "OPTIONS") { Response.StatusCode = (int)HttpStatusCode.OK; Response.AppendHeader("Access-Control-Allow-Origin", Request.Headers.GetValues("Origin")[0]); Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept"); Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); Response.AppendHeader("Access-Control-Allow-Credentials", "true"); Response.End(); } } 

What this code does is add the missing headers to the OPTIONS (preview) response, which cause an error before the flight. Since I have different roots calling my web API, I use Request.Headers.GetValues("Origin")[0]) to set the source to the response dynamically.

In WebApiConfig.cs, I still specified different roots, but used wildcards in the headers and methods, and also to set SupportsCredentials to true:

 var cors = new EnableCorsAttribute("http://localhost:63342,http://localhost:63347,http://localhost:63345", "*", "*"); cors.SupportsCredentials = true; config.EnableCors(cors); 

Also, if you use AngularJS like me, you must configure $ http to use credentials. This can be configured globally as follows:

 angular .module('Application') .config(['$httpProvider', function config($httpProvider) { $httpProvider.defaults.withCredentials = true; } ]); 

What is it. This solved my problem. If anyone else has problems, I recommend reading the following publications that helped me answer:

+7
source

Create your own attribute using ICorsPolicyProvider something like the following to check if the requested start is allowed or not

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,AllowMultiple = false)] public class EnableCorsForAPIKeysAttribute : Attribute, ICorsPolicyProvider, IFilter { public async Task<CorsPolicy> GetCorsPolicyAsync( HttpRequestMessage request, CancellationToken cancellationToken) { var corsRequestContext = request.GetCorsRequestContext(); var originRequested = corsRequestContext.Origin; if (await IsValidOrigin(originRequested)) //Check if requested origin is valid or not { // Grant CORS request var policy = new CorsPolicy { AllowAnyHeader = true, AllowAnyMethod = true }; policy.Origins.Add(originRequested); return policy; } else { // Reject CORS request return null; } } public bool AllowMultiple { get {return false;} } } 

To use it, add it to your API controller.

 [EnableCorsForAPIKeys] public class APIBaseController : ApiController { } 
+1
source

All Articles