【问题标题】:ServiceStack, CORS, and OPTIONS (No Access-Control-Allow-Origin header)ServiceStack、CORS 和 OPTIONS(无 Access-Control-Allow-Origin 标头)
【发布时间】:2015-03-05 22:59:17
【问题描述】:

我们在 ServiceStack 4 中使用 RESTful API 的 CORS 功能遇到了一些障碍。

我们希望将 cookie 发送到 api,因为 SS 会话在 cookie 中,因此我们在命中 API 的 Angular 客户端中使用“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 服务在请求结束时没有返回 Access-Control-Allow-Origin 标头。这会使 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 上找不到类似的东西。

我们是否对 OPTIONS 预请求过滤器做错了什么?为什么它不返回 Access-Control-Allow-Origin 标头?

【问题讨论】:

  • 感谢上帝的帖子,我对 CORS 感到疯狂。你用你的例子帮助我,并说用 angularjs 打开 WithCredentials !谢谢

标签: c# http-headers cors servicestack


【解决方案1】:

注意: 更新到 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" }));

【讨论】:

  • @mythz 知道为什么我们在仅使用上面列出的 CorsFeature 插件而没有添加 OPTIONS 重新请求过滤器的情况下会在响应中获得双倍的 ACAO 标头吗?
  • 您使用的是latest v4.0.36 release on MyGet吗?我的回答解释了为什么 v4.0.35 将标头写入两次,并且已通过 v4.0.36 that's on MyGet 解决。我不明白你为什么需要注册自己的 PreRequestFilters,因为这已经在 CorsFeature 中处理了。
  • @mythz 我们在 NuGet 上拥有最新版本,而不是 MyGet。在 MyGet 上更新为使用临时版本解决了双标题问题。现在 auth 工作了,但是在 auth 之后对服务调用的 GET 没有返回 ACAO 标头,所以它失败了。服务调用非常简单;它实际上是一个测试调用(基本上,为经过身份验证的用户返回用户会话对象)。
  • @mythz 没关系;我们的开发人员发现了问题; 4.0.36 更新绝对解决了这个问题,并使使用 allowOriginWhitelist 成为一个干净且可行的解决方案。谢谢!
【解决方案2】:

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

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

在 v4.0.35 中引入了一个问题,PreRequestFilters were being written in Custom HttpHandlers 导致 CorsFeature 两次写出 Access-Control-Allow-Origin 标头,导致浏览器拒绝它。这个问题现在已经解决了in this commit,可以从 v4.0.36+ 获得,现在是available on MyGet

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

<!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 registration in above Test project的源代码:

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

【讨论】:

  • 使用这个白名单意味着我们每次添加新的 api 消费者时都要重新部署服务,对吧?我希望通过 prerequest 过滤器来避免这种情况,它适用于 GET,但不会被用于 OPTION。
  • @gizmoboy 白名单集合是公开的,因此您可以在运行时添加它:HostContext.GetPlugin&lt;CorsFeature&gt;().AllowOriginWhitelist.Add("http://newdomain.com");
  • @gizmoboy 否则CorsFeature 非常轻量级,使用本地自定义版本是另一个潜在的选择。
  • 对此非常困惑。 plunkr 工作正常,但是当我尝试在我们的 SPA 中运行您的 $http 调用时,我在 Chrome 控制台中收到此错误:XMLHttpRequest cannot load test.servicestack.net/auth/credentials。请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,Origin 'localhost:9000' 不允许访问。
  • 我尝试添加白名单,但对 auth 的 OPTIONS 调用仍未返回 access-control-allow-origin 标头。
猜你喜欢
  • 2017-11-24
  • 2018-09-19
  • 2019-06-19
  • 2019-02-07
  • 2021-08-12
  • 2018-03-28
  • 1970-01-01
  • 1970-01-01
  • 2021-07-15
相关资源
最近更新 更多