ServiceStack, CORS, and OPTIONS (No Access-Control-Allow-Ori

本文关键字:No Access-Control-Allow-Ori and CORS ServiceStack OPTIONS | 更新日期: 2023-09-27 18:36:35

我们在ServiceStack 4中对Restful API的CORS功能遇到了一些障碍。

我们希望将 cookie

发送到 api,因为 SS 会话在 cookie 中,因此我们在命中 API 的角度客户端中使用 "WithCredentials" = true 进行 AJAX 调用。

由于Chrome(至少)不喜欢带有WithCredentials的Access-Control-Allow-Origin通配符,因此我们添加了一个预请求过滤器,以在Access-Control-Allow-Origin标头中回显请求者的来源,如下所示:

private void ConfigureCors()
            {
                Plugins.Add(new CorsFeature(
                    allowedHeaders: "Content-Type",
                    allowCredentials: true,
                    allowedOrigins: ""));
                PreRequestFilters.Add((httpReq, httpRes) =>
                {
                    string origin = httpReq.Headers.Get("Origin");
                    if (origin != null)
                    {
                        httpRes.AddHeader(HttpHeaders.AllowOrigin, origin);
                    }
                    else
                    {
                        // Add the dev localhost header.
                        httpRes.AddHeader(HttpHeaders.AllowOrigin, "http://localhost:9000");
                    }
                });
                PreRequestFilters.Add((httpReq, httpRes) =>
                {
                    //Handles Request and closes Responses after emitting global HTTP Headers
                    if (httpReq.Verb == "OPTIONS")
                    {
                        httpRes.EndRequest();
                    }
                });
            }

但是,我们在 OPTIONS 请求上遇到了障碍,因为当请求结束时,SS 服务不会发出访问控制允许源标头。这会使 Chrome 拒绝该来电。

我们尝试在 OPTIONS 的预请求过滤器中放置一个显式标头,但它仍然没有返回 OPTIONS 调用的 ACAO 标头:

 PreRequestFilters.Add((httpReq, httpRes) =>
            {
                //Handles Request and closes Responses after emitting global HTTP Headers
                if (httpReq.Verb == "OPTIONS")
                {
                    httpRes.AddHeader(HttpHeaders.AllowOrigin, "*");
                    httpRes.EndRequest();
                }
            });

看起来这之前一定已经处理过了,但我们在StackOverflow上找不到这样的东西。

我们在选项预请求过滤器方面做错了什么吗?为什么它不返回访问控制允许源标头?

ServiceStack, CORS, and OPTIONS (No Access-Control-Allow-Ori

您应该在allowOriginWhitelist中添加列入白名单的域,例如:

Plugins.Add(new CorsFeature(allowedHeaders:"Content-Type",
    allowCredentials:true,
    allowOriginWhitelist:new[]{"http://localhost:9000"}));

v4.0.35 中引入了一个问题,即由于 PreRequestFilters 是在自定义 HttpHandler 中编写的,这导致 CorsFeature 写出 Access-Control-Allow-Origin 标头两次,导致浏览器拒绝它。此问题现已在此提交中得到解决,该提交可从 v4.0.36+ 获得,现在可在 MyGet 上使用。

此最新版本已部署到 http://test.servicestack.net 演示中,该演示显示了在此jsbin:http://jsbin.com/korijigucu/1/edit 中使用ServiceStack进行跨域身份验证

<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <script>
    var apiBase = "http://test.servicestack.net";
    var app = angular.module('app', []);
    app.run(['$rootScope', '$http', function ($rootScope, $http) {  
      $rootScope.success = "running...";
      $http
         .post(apiBase + '/auth/credentials', 
             { "UserName": "test", "Password": "test" }, 
             { withCredentials: true })
         .success(function (data) {
             $rootScope.success = "Login successful: " + JSON.stringify(data);
         })
         .error(function (data, status, headers, config) {
             $rootScope.error = 'ERR:login';
         });
    }]);    
  </script>
</head>
<body>    
  <div style='color:green'>{{success}}</div>
  <div style='color:red'>{{error}}</div>
</body>
</html>

上述测试项目中 CorsFeature 注册的源代码:

Plugins.Add(new CorsFeature(
    allowOriginWhitelist: new[] { 
      "http://localhost", "http://localhost:56500", 
      "http://test.servicestack.net", "http://null.jsbin.com" },
    allowCredentials: true,
    allowedHeaders: "Content-Type, Allow, Authorization"));

注意: 更新到 4.0.36 解决了下面描述的双标头问题,使第二个预请求过滤器过时。

我终于让这个工作了,但它感觉就像一个笨蛋。

我按照 Demis 的建议添加了 allowOriginWhitelist 条目,该条目为标头返回双倍值 ( Access-Control-Allow-Origin:http://localhost:9000 ),至少对于 OPTIONS 调用(它似乎适用于 POST 和 GET):

 Plugins.Add(new CorsFeature(
                    allowedHeaders: "Content-Type, Allow, Authorization",
                    allowCredentials: true,
                    allowOriginWhitelist: new[] { "http://localhost:9000", "http://www.productiondomain.com", "https://www.productiondomain.com" }));

因此,我根据我们之前使用的过滤器添加了以下预请求过滤器:

PreRequestFilters.Add((httpReq, httpRes) =>
                {
                    //Handles Request and closes Responses after emitting global HTTP Headers
                    if (httpReq.Verb == "OPTIONS")
                    {
                        string origin = httpReq.Headers.Get("Origin");
                        if (origin != null)
                        {
                            httpRes.AddHeader(HttpHeaders.AllowOrigin, origin);
                        }
                        else
                        {
                            // Add the dev localhost header.
                            httpRes.AddHeader(HttpHeaders.AllowOrigin, "http://localhost:9000");
                        }
                        httpRes.EndRequest();
                    }
                });

这已经解决了问题,但感觉就像一个笨蛋。有谁知道为什么这个简单的代码会在响应中产生双倍的 ACAO 标头?

     Plugins.Add(new CorsFeature(
                    allowedHeaders: "Content-Type, Allow, Authorization",
                    allowCredentials: true,
                    allowOriginWhitelist: new[] { "http://localhost:9000", "http://www.productiondomain.com", "https://www.productiondomain.com" }));