使用具有多个来源的 PUT 和 DELETE 请求时如何解决 ASP.NET Web API CORS Preflight 问题?

Posted

技术标签:

【中文标题】使用具有多个来源的 PUT 和 DELETE 请求时如何解决 ASP.NET Web API CORS Preflight 问题?【英文标题】:How to solve ASP.NET Web API CORS Preflight issue when using PUT and DELETE requests with multiple origins? 【发布时间】:2017-01-29 13:02:26 【问题描述】:

我有一个 ASP.NET Web API,它被三个不同的 SPA 调用。我正在为 Web API 使用 Windows 身份验证。我最初尝试像这样在 Web.config 中配置 CORS:

<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>

这导致了这个预检问题:

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.

我通过在 Global.asax.cs 中添加以下方法来解决:

protected void Application_BeginRequest()

    if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
    
        Response.Flush();
    

这种方法非常适合单个 SPA。我想我可以去 Web.config 并像这样添加其他来源:

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

但显然这是不允许的。这产生了以下错误:

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

所以为了尝试解决这个问题,我改变了方法,而是决定尝试在 WebAPIConfig.cs 上配置 CORS,在 Register 方法中,如下所示:

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);

我认为这会起作用,但现在我在使用 PUT 和 DELETE 请求时再次遇到预检错误,我不知道如何解决这个问题。我调试了 Application_BeginRequest 方法,它仍在刷新 OPTIONS 请求,所以我不知道是什么导致了这个错误。有谁知道我该如何解决这个问题?

编辑:

预检错误的打印:

【问题讨论】:

@Aravind 很抱歉,我无法真正理解您建议的解决方案,因为由于某种原因,所以引号会加倍。能不能换个方式写? 您可以使用new EnableCorsAttribute("*","*","*"); 允许所有域和方法,更好的处理方法是使用 ICorsPolicyProvider 创建您自己的自定义属性并实现 GetCorsPolicyAsync 方法。 @Paresh 但我不能使用 * 因为我使用的是 Windows 身份验证并且它不兼容。除此之外,我只想允许这三个网址。 使用 * 作为标头和方法,让 url 保持原样。新的 EnableCorsAttribute("localhost:63342, localhost:63347, localhost:63345","",""); @Aravind 我试过了,但没用。仍然收到预检错误。 【参考方案1】:

使用ICorsPolicyProvider 创建自定义属性,如下所示检查请求的来源是否被允许

    [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; 
    

要使用它,请将其添加到您的 API 控制器中

[EnableCorsForAPIKeys]
public class APIBaseController : ApiController


【讨论】:

这个解决方案不会让我必须将该注释添加到所有控制器吗?我宁愿有一个可以全局配置所有内容的解决方案。 我认为你可以在全球范围内使用config.Filters.Add(new EnableCorsForAPIKeysAttribute());【参考方案2】:

我能够通过进一步自定义 Global.asax.cs 中的 Application_BeginRequest 方法来解决我的问题,如下所示:

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();
    

此代码的作用是将丢失的标头添加到导致预检错误的 OPTIONS 响应(预检请求)中。由于我有不同的来源调用我的 Web API,我使用Request.Headers.GetValues("Origin")[0]) 在响应中动态设置来源。

在 WebApiConfig.cs 中,我仍然指定了不同的来源,但在标头和方法上使用了通配符,并将 SupportsCredentials 设置为 true,如下所示:

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

另外,如果您像我一样使用 AngularJS,则必须配置 $http 以使用凭据。这可以像这样全局配置:

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

就是这样。这解决了我的问题。如果其他人仍有问题,我建议阅读以下出版物,这有助于我找到答案:

http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api https://evolpin.wordpress.com/2012/10/12/the-cors/ AngularJS $http, CORS and http authentication AngularJS performs an OPTIONS HTTP request for a cross-origin resource AngularJS POST Fails: Response for preflight has invalid HTTP status code 404

【讨论】:

以上是关于使用具有多个来源的 PUT 和 DELETE 请求时如何解决 ASP.NET Web API CORS Preflight 问题?的主要内容,如果未能解决你的问题,请参考以下文章

PUT/DELETE 出现 403 错误,但 GET/POST 有效

Javascript:获取 DELETE 和 PUT 请求

AJAX:POST-PUT-DELETE的问题

PUT 和 DELETE HTTP 请求方法有啥用处?

http中的put,delete等请求为啥不安全

(转)SpringMvc如何使用form发送PUT和DELETE请求