获取 CORS 未获取标题的 Post 问题

Posted

技术标签:

【中文标题】获取 CORS 未获取标题的 Post 问题【英文标题】:Fetch Post issues with CORS not getting header 【发布时间】:2017-07-21 11:25:29 【问题描述】:

这个 CORS 让我再次跪下。我的意思是它可能是如此令人沮丧。请理解,在您对我投反对票之前,我已经查看了有关该主题的所有 500 万篇帖子。我意识到关于这个主题有很多。这是我的 React UI 代码中的 Fetch Post。这是在带有已编译 JS 的 IIS 服务器上运行的,并且只是用于 SPA 的 Index.html。我试图在同一服务器的不同端口上调用 API。这是在 Chrome 和其他现代浏览器中杀死我的预检(在 IE 中似乎很好)。

这里是获取:

    return (
        fetch(mypost, 
            method: 'POST',
            headers: 
                "Content-Type": "application/json",
                "Access-Control-Allow-Origin": "*",
                "Accept": "application/json",
            ,
            mode: 'cors',
            body: JSON.stringify(
                name: this.state.value,
            )
        ).then(response => 
            if (response.status >= 400) 
                this.setState(
                    value: 'no greeting - status > 400'
                );
                throw new Error('no greeting - throw');
            
            return response.text()
        ).then(data => 
            var myData = JSON.parse(data);
            this.setState(
                greeting: myData.name,
                path: myData.link
            );
        ).catch(() => 
            this.setState(
                value: 'no greeting - cb catch'
            )
        )
    );

我们都见过的标准预检错误。

Fetch API 无法加载 http://myApiServer:81/values/dui/。对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,Origin 'http://localhost:82' 不允许访问。如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以获取禁用 CORS 的资源。

这是预检的小提琴:

OPTIONS http://myApiServer:81/values/dui/ HTTP/1.1
Host: myApiServer:81
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:82
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Access-Control-Request-Headers: access-control-allow-origin, content-type
Accept: */*
Referer: http://localhost:82/validator
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

现在我了解到设置 no-cors 基本上会设置一个默认标头,这也不是我想要的,我需要我的 application/json,因为谁不想要 JSON,对吧? :)

我很想就如何解决这个问题提供任何建议。基本上,由于这只是在 IIS 服务器上编译的 javascript 和 index.html,我需要知道处理这些似乎正在发生的预检选项检查的最佳解决方案。

******更新

我尝试添加 webconfig 以强制 IIS 服务器处理预检。似乎它需要在两端,我的 UI 和 API?

这是我的 UI Web.Config

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
    <httpProtocol>
        <customHeaders>
            <add name="Access-Control-Allow-Origin" value="*" />
            <add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE,OPTIONS" />
            <add name="Access-Control-Allow-Headers" value="Content-Type" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

还有我的 WebApi web.config

  <?xml version="1.0" encoding="utf-8"?>
<configuration>

<system.webServer>
  <handlers>
    <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
  </handlers>
  <httpProtocol>
    <customHeaders>
     <add name="Access-Control-Allow-Origin" value="*" />
     <add name="Access-Control-Allow-Headers" value="Content-Type" />
     <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE" />
    </customHeaders>
  </httpProtocol>
  <aspNetCore processPath="dotnet" arguments=".\dui.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="true" />
 </system.webServer>
 </configuration>

我现在得到的只是:

获取 API 无法加载 http://myApiServer:81/values/dui/。预检响应包含无效的 HTTP 状态代码 415。

这里是这个的小提琴。

请求

OPTIONS http://myApiServer:81/values/dui/ HTTP/1.1
Host: myApiServer:81
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:82
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Access-Control-Request-Headers: content-type
Accept: */*
Referer: http://localhost:82/validator
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

和回应

HTTP/1.1 415 Unsupported Media Type
Content-Length: 0
Server: Kestrel
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Date: Thu, 02 Mar 2017 18:35:31 GMT

【问题讨论】:

从您的 headers 值中删除 "Access-Control-Allow-Origin": "*", 行。 Access-Control-Allow-Origin 是一个 response 标头。如果你把它放在一个请求中,它除了触发一个预检之外没有任何作用,如果没有被触发的话。 您是否拥有对 http://server:81/values/dui/ 的服务器环境的管理员访问权限,以便您可以将其配置为在响应中发送 CORS 响应标头? @sideshowbarker 我在离开工作之前尝试从我的请求中删除访问控制允许但没有更改。我认为应用程序/Json 可能仍会强制进行预检?我在 IIS 服务器上确实具有管理员访问权限,所以我明天可以尝试任何事情。让我知道。谢谢大哥! application/json 内容类型的浏览器标头让浏览器做一个预检是的 @sideshowbarker 所以我一直在搞乱 IIS 服务器,更重要的是现在直接使用 web.config 来设置 IIS 中的响应标头。我感觉就像一条追着尾巴的狗。关于 customHeaders,我的 API 和 UI 设置都相同(请参阅 webconfigs 的更新代码)。我的 api 也已经有一些代码来处理 CORS。主要是'services.AddCors();'和'app.UseCors(builder => builder.AllowAnyOrigin());'那一秒似乎导致了这个错误。 (“Access-Control-Allow-Origin”标头包含多个值“*、*”,但只允许一个。) 【参考方案1】:

正如某人在另一篇文章中所建议的那样,假设您没有使用 Nodejs,请对您的服务器端执行等效操作。请注意,我遇到类似问题的客户端是 Reactjs 驱动的,这让我们甚至

app.use(function (req, res, next) 

    // Website you wish to allow to connect
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8888');

    // Request methods you wish to allow
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');

    // Request headers you wish to allow
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');

    // Set to true if you need the website to include cookies in the requests sent
    // to the API (e.g. in case you use sessions)
    res.setHeader('Access-Control-Allow-Credentials', true);

    // Pass to next layer of middleware
    next();
);

【讨论】:

【参考方案2】:

我已经摸索了 2 个小时,终于找到了适合我的解决方案。如果您和我一样处于同样的情况,我希望我的回答可以帮助您节省时间。由于我可以控制 Web API (aspnet core) 和客户端 (fetch),因此我执行了以下操作:

首先,实际上 真的了解 CORS 很重要。我建议你预留 15~30 分钟的时间,这样可以节省你的时间,相信我。作为Puerto mentioned,阅读文档:https://docs.microsoft.com/en-us/aspnet/core/security/cors 在我看来,关于 CORS 的 Microsoft 文档对于我们 .NET 开发人员来说更容易理解。如果你愿意,你也可以阅读MDN docs,但我很难理解并将这些部分放在一起。

现在我们对 CORS 有了一些了解,请在您的 Startup.cs 文件中执行以下操作:

public void ConfigureServices(IServiceCollection services)

    services.AddCors();


public void Configure(IApplicationBuilder app, IHostingEnvironment environment)

    app.UseCors(builder =>
    
        builder.WithOrigins("http://yourclient/");
        builder.AllowAnyMethod();
        builder.AllowAnyHeader();
    );

    app.UseMvc();

现在在客户端,这样做:

fetch("http://yourapi/api/someresource", 
    method: "POST",
    headers: 
        "Content-Type": "application/json",
        "Access-Control-Request-Headers": "*",
        "Access-Control-Request-Method": "*"
    ,
    body: JSON.stringify(data)
)...;

就是这样。

【讨论】:

显然您必须安装 Microsoft.AspNetCore.Cors 包才能在服务器上启用 CORS。 @QuantumHive-使用 application/json 您将生成一个预检响应。我很惊讶您不需要服务器上的 webconfig 来处理预检。您是在 IIS 中作为容器还是标准应用程序运行? @Puerto 我不确定您所说的“容器”或“标准应用程序”是什么意思。也许我应该提到我正在使用dotnet core 1.1。据我从文档中得知,您唯一需要做的就是使用app.UseCors()services.AddCors() 启用CORS。我只是在 IIS 后面使用 Kestrel 服务器。这是从 Visual Studio 模板创建新的 aspnet core webapp 项目时的默认设置。 你回答了我的问题。您在 IIS 上运行(使用 Kestrel,因为您是 dotnet 核心)。但是您可以将 kestrel 作为跨平台容器化应用程序运行,例如在 OpenShift 中运行。但既然你不是,我很好奇你如何能够绕过在 IIS 服务器上不设置任何东西来处理你的应用程序/json 请求肯定会生成的预检请求。文档可能会说您只需要这两件事,但如果您正在生成预检请求,我认为这并不完全正确。 啊,我明白了。在aspnet core 中,IIS just 将 HTTP 请求委托给 Kestrel。我最近了解到HTTP Modules and Handlers(我们习惯于在 IIS 和 ASP.NET 中使用)不再适用于aspnet core 应用程序。 HTTP 请求由 Kestrel 在我们使用中间件定义的管道中处理(例如app.UserCors())。最初,整个 HTTP 管道只是空的。但我不完全确定 IIS 在将请求委托给 Kestrel 之前会执行什么样的业务。【参考方案3】:

我已经为我的 dotnet core webAPI 切换到这个解决方案。我发现它更干净,因为我不需要 webconfig 和 cors 配置,它需要设置得恰到好处才能让 2 一起工作。这消除了 webConfig 中的 cors 内容,因此您只需将其设置在一个地方,即您的 API 应用程序代码本身。

https://www.codeproject.com/Articles/1150023/Enable-Cross-origin-Resource-Sharing-CORS-in-ASP-N

只是对链接中的内容进行简要总结。

添加到 project.json

//Cross Origin Resource Sharing
"Microsoft.AspNetCore.Cors": "1.0.0"

在启动时将其添加到您的 ConfigureServices。

services.AddCors(
options => options.AddPolicy("AllowCors",
builder =>

    builder
    //.WithOrigins("http://localhost:4456") //AllowSpecificOrigins;
    //.WithOrigins("http://localhost:4456", 
    "http://localhost:4457") //AllowMultipleOrigins;
    .AllowAnyOrigin() //AllowAllOrigins;

    //.WithMethods("GET") //AllowSpecificMethods;
    //.WithMethods("GET", "PUT") //AllowSpecificMethods;
    //.WithMethods("GET", "PUT", "POST") //AllowSpecificMethods;
    .WithMethods("GET", "PUT", 
    "POST", "DELETE") //AllowSpecificMethods;
    //.AllowAnyMethod() //AllowAllMethods;

    //.WithHeaders("Accept", "Content-type", "Origin", "X-Custom-Header");  
    //AllowSpecificHeaders;
    .AllowAnyHeader(); //AllowAllHeaders;
)
);

也可以在启动时配置

//Enable CORS policy "AllowCors"
    app.UseCors("AllowCors");

然后在你的控制器中确保你有这些引用:

 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Cors;
 using CrossOrigin.WebService.Models.DbEntities;
 using Microsoft.EntityFrameworkCore;

最后将属性添加到您的控制器类中。

[EnableCors("AllowCors"), Route("api/[controller]")]
 public class ContactController : Controller

【讨论】:

【参考方案4】:

所以我要发布这个答案,因为它实际上现在对我有用。这就是我所经历的。我不完全确定为什么,但我会尽力解释我在此过程中学到的东西。如果有人愿意纠正或进一步解释,我会很乐意给予信任和支持。

因此,在 API 中似乎存在 3 层 CORS 感知。

第 1 层

API 本身的代码可能会有一些 CORS 设置,您也应该密切注意这些设置,并且一定会阅读。就我而言,标准位置位于 webApi 的 Startup.cs 中。我的 WebApi 中一直有这些,坦率地说,我认为这就是最初所需要的(我几乎不知道)。

services.AddCors();
app.UseCors(builder => builder.AllowAnyOrigin())

我还在我的 webconfig 中设置了它,这将我们带到:

第 2 层

<customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
    <add name="Access-Control-Allow-Headers" value="Content-Type" />
    <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE" />
</customHeaders>

我之所以使用它,是因为一位智者说(感谢@sideshowbarker)我的 React UI 中的 Fetch 正在生成一个自定义标头,因此在我的服务器上生成了一个预检响应,因此需要在服务器级别处理该响应.

第 3 层

所以在我的情况下,第 3 层确实不是问题,但在此过程中您必须注意这一点。所以不是在 API 代码中,不是在服务器上,而是在 UI(调用应用程序)本身中。

一般来说,除了"Access-Control-Allow-Origin": "*" 之外,我这里还有正确的东西,这显然不是所有地方都需要的,因为这是响应标头,并且在获取时我指定了请求标头,再次感谢@sideshowbarker。

所以我的 Fetch 请求如下所示:

fetch(mypost, 
    method: 'POST',
    headers: 
        "Content-Type": "application/json"
    ,
    mode: 'cors',
    body: JSON.stringify(
        name: this.state.value,
    )
)

在我的情况下,主要是因为我是 'context-type: application/json' 导致了预检响应。我想这不是标准的。在当今世界对我来说似乎很标准,但我知道什么。

无论如何,尽管如此,我仍然收到以下消息,坦率地说,这比看到预检错误要好。进步就是进步。

Access-Control-Allow-Origin' 标头包含多个值 '*, *', 但只允许一个。

我在网上发现了一些东西,提到我不需要在两个地方(网络服务器和 API)AllowAnyOrigin,所以我尝试评论这段代码。

//app.UseCors(builder => builder.AllowAnyOrigin());

得到:

Fetch API 无法加载 http://ApiServer:81/values/dui/。回应 预检具有无效的 HTTP 状态代码 415

然后根据一些搜索发现一篇文章说最后一个错误可能与标题有关。我回到了 API 源 dotnet Core cors。

https://docs.microsoft.com/en-us/aspnet/core/security/cors

这是一个很好的参考。我应该更加关注这一点。它并没有完全解决我的问题,但我注意到还有另一个构建器可以用于标题,所以我将其插入、重建、发布和 bam,不再有 CORS 问题。一切正常!

这是我插入 API 的代码:

app.UseCors(builder => builder.AllowAnyHeader());

对于第 2 层和第 3 层,我的所有其余代码都保持不变。我将第 1 层的 API 中的 AllowAnyOrigin 构建器注释掉了。

我很抱歉在这里过于冗长,但我发现 CORS 很痛苦,我希望这对某人有所帮助。我会说是时候喝 Coors 了,但我想我们都知道啤酒很糟糕。我想这还是有道理的。

【讨论】:

以上是关于获取 CORS 未获取标题的 Post 问题的主要内容,如果未能解决你的问题,请参考以下文章

获取无效 POST、PATCH 的 CORS 错误 - REST 端点

在前端的 HTTP Post 调用上获取 CORS 错误

如何正确设置 GET/POST 获取的标头以避免 CORS

无法在标头中没有 no-cors 的情况下获取 POST

在 Django 中使用 Axios 和 CORS 获取 POST 请求的错误请求并做出反应

在跨域ajax post中的回调函数中获取未定义