处理对ASP.NET MVC操作的CORS预检请求

Car*_*cke 50 asp.net-mvc cors

我正在尝试对ASP.NET MVC控制器操作执行跨域POST请求.此控制器操作接受并使用各种参数.问题是,当预检请求发生时,控制器操作实际上会尝试执行,因为OPTIONS请求没有传递任何数据,控制器操作会抛出500 HTTP错误.如果我删除使用该参数的代码或参数本身,则整个请求链成功完成.

这是如何实现的一个例子:

控制器动作

public ActionResult GetData(string data)
{
    return new JsonResult
    {
        Data = data.ToUpper(),
        JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
}
Run Code Online (Sandbox Code Playgroud)

客户端代码

<script type="text/javascript">
        $(function () {
            $("#button-request").click(function () {
                var ajaxConfig = {
                    dataType: "json",
                    url: "http://localhost:8100/host/getdata",
                    contentType: 'application/json',
                    data: JSON.stringify({ data: "A string of data" }),
                    type: "POST",
                    success: function (result) {
                        alert(result);
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        alert('Error: Status: ' + textStatus + ', Message: ' + errorThrown);
                    }
                };

                $.ajax(ajaxConfig);
            });
        });
    </script>
Run Code Online (Sandbox Code Playgroud)

现在,每当预检请求发生时,它返回500个HTTP代码,因为"data"参数为null,因为OPTIONS请求没有传递任何值.

服务器应用程序已在端口8100上的本地IIS中设置,并且在端口8200上设置运行客户端代码的页面以模拟跨域调用.

我还使用以下标头配置了主机(在8100上):

Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: POST, GET
Access-Control-Allow-Origin: http://localhost:8200
Run Code Online (Sandbox Code Playgroud)

我发现的一个解决方法是检查执行操作的HTTP方法,如果是OPTIONS请求只返回空白内容,否则执行操作代码.像这样:

public ActionResult GetData(string data)
{
    if (Request.HttpMethod == "OPTIONS") {
        return new ContentResult();
    } else {
        return new JsonResult
        {
            Data = data.ToUpper(),
            JsonRequestBehavior = JsonRequestBehavior.AllowGet
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

但这种方法对我来说非常笨重.我考虑过将这种逻辑添加到一个Attribute,但即使这样也意味着要使用CORS来装饰将被调用的所有动作.

是否有一个更优雅的解决方案来使这个功能工作?

Car*_*cke 63

所以我找到了一个有效的解决方案.对于每个请求,我检查它是否是CORS请求以及请求是否带有OPTIONS动词,表明它是预检请求.如果是,我只是发回一个空响应(当然只包含IIS中配置的头),从而否定了控制器动作的执行.

然后,如果客户端确认允许基于预检返回的头执行请求,则执行实际的POST并执行控制器操作.我的代码示例:

protected void Application_BeginRequest()
{
    if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) &&
        Request.HttpMethod == "OPTIONS") {
        Response.Flush();
    }
}
Run Code Online (Sandbox Code Playgroud)

如上所述,这对我有用,但如果有人知道更好的方式,或者我当前实施中的任何缺陷,我将很高兴听到他们.

  • 这对我来说不太有用,但是调试和检查让我发现了这个小小的变化.添加特定标头,并确保**明确**命名Access-Control-Allow-Headers(星号不够).`if(Request.Headers.AllKeys.Contains("Origin")&& Request.HttpMethod =="OPTIONS"){Response.Headers.Add("Access-Control-Allow-Origin","*"); Response.Headers.Add("Access-Control-Allow-Headers","Content-Type,Accept,X-Requested-With,Session"); Response.Flush(); }` (4认同)
  • 这将进入global.asax并且是您可以挂钩的应用程序级事件之一. (3认同)
  • 标头键不区分大小写,我遇到过 Chrome 中的 jQuery 将发送“Origin”(对于 HTTP)和“origin”(对于 HTTPS)的情况。但 jQuery 和 Chrome 的组合不太可能做到这一点。我已经更新了您的答案,以不区分大小写的方式进行检查,以解决这种差异。 (2认同)

Jon*_*lis 11

扩展了Carl的答案,我把他的代码插入我的OWIN管道:

app.Use((context, next) =>
{
     if (context.Request.Headers.Any(k => k.Key.Contains("Origin")) && context.Request.Method == "OPTIONS")
     {
         context.Response.StatusCode = 200;
         return context.Response.WriteAsync("handled");
     }

     return next.Invoke();
});
Run Code Online (Sandbox Code Playgroud)

只需将它添加到Startup.cs中的IAppBuilder的开头(或者在注册WebAPI之前的任何地方)


Gab*_*uez 7

接受的答案就像一个超级按钮一样工作,但是我发现请求实际上已经传递给了控制器。我正在接收200状态代码,但是响应主体包含许多HTML,但控制器中有异常。因此Response.Flush(),我发现最好使用而不是使用Response.End(),它确实会停止执行请求。此替代解决方案如下所示:

编辑:修复了原始答案中的错字。

protected void Application_BeginRequest()
{
    if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) &&
        Request.HttpMethod == "OPTIONS") {
        Response.End();
    }
}
Run Code Online (Sandbox Code Playgroud)


Rob*_*b L 5

这是我使用ASP.Net Web Api处理预检/ CORS问题的方式。我只是将Microsoft.AspNet.WebApi.Cors Nuget包添加到我的Web项目中,然后在我的WebApiConfig.cs文件中添加以下行:

config.EnableCors(new ApplicationCorsPolicy());
Run Code Online (Sandbox Code Playgroud)

并创建了一个自定义PolicyProvider类

public class ApplicationCorsPolicy : Attribute, ICorsPolicyProvider
{
    public async Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var corsRequestContext = request.GetCorsRequestContext();
        var originRequested = corsRequestContext.Origin;

        if (await IsOriginFromAPaidCustomer(originRequested))
        {
            // Grant CORS request
            var policy = new CorsPolicy
            {
                AllowAnyHeader = true,
                AllowAnyMethod = true
            };
            policy.Origins.Add(originRequested);
            return policy;
        }
        // Reject CORS request
        return null;
    }

    private async Task<bool> IsOriginFromAPaidCustomer(string originRequested)
    {
        // Do database look up here to determine if origin should be allowed.
        // In my application I have a table that has a list of domains that are
        // allowed to make API requests to my service. This is validated here.
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

看到了,Cors框架允许您添加自己的逻辑来确定允许哪些来源,等等。如果要将REST API暴露给外界并且可以访问您的站点的人员(来源)列表是非常有用的,这将非常有用。在像数据库这样的受控环境中。现在,如果您只是允许所有来源(在所有情况下可能都不是一个好主意),则可以在WebApiConfig.cs中执行此操作以全局启用CORS:

config.EnableCors();
Run Code Online (Sandbox Code Playgroud)

就像WebApi中的过滤器和处理程序一样,您也可以向控制器添加类或方法级别的注释,如下所示:

[EnableCors("*, *, *, *")]
Run Code Online (Sandbox Code Playgroud)

请注意,EnableCors属性的构造函数接受以下参数

  1. 允许的起源清单
  2. 允许的请求标头列表
  3. 允许的HTTP方法列表
  4. 允许的响应头列表

您可以在每个控制器/端点上静态指定允许访问哪些资源的人。

更新06/24/2016: 我应该提到,我的Web.config中包含以下内容。看起来这些并不是所有人的默认设置。

<system.webServer>
    <handlers>
        <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
        <remove name="OPTIONSVerbHandler" />
        <remove name="TRACEVerbHandler" />
        <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
        </handlers>
</system.webServer>
Run Code Online (Sandbox Code Playgroud)

资料来源:微软


归档时间:

查看次数:

39668 次

最近记录:

6 年,2 月 前