Fetch Post CORS 未获取 header 的问题

Fetch Post issues with CORS not getting header

这个 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/. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:82' 因此不允许访问。如果不透明响应满足您的需求,请将请求的模式设置为 'no-cors' 以在禁用 CORS 的情况下获取资源。

这是预检的fiddle:

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基本上会设置一个默认的header,这也不是我想要的,我需要我的application/json,因为谁不想要JSON,对吧? :)

我很乐意就如何解决此问题提出任何建议。基本上,因为这只是在 IIS 服务器上编译 Javascript 和 index.html,所以我需要知道处理这些似乎正在发生的预检选项检查的最佳解决方案。

******更新

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

这是我的UIWeb.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。

这里是fiddle。

要求

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

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

所以在 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 正在生成自定义 header 并因此在我的服务器上生成预检响应所以这是需要在服务器级别处理响应。

第 3 层

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

一般来说,除了 "Access-Control-Allow-Origin": "*" 之外,我在这里还有正确的东西,显然这里不需要所有地方,因为这是响应 header 并且在获取中我指定了请求 headers,再次感谢@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' header contains multiple values '*, *', but only one is allowed.

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

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

并得到:

Fetch API cannot load http://ApiServer:81/values/dui/. Response for preflight has invalid HTTP status code 415

然后根据一些搜索发现一篇文章说最后一个错误可能是 header-related。我回到 API source dotnet Core cors。

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

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

这是我插入 API:

的代码
app.UseCors(builder => builder.AllowAnyHeader());

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

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

我已经为我的 dotnet 核心 webAPI 切换到这个解决方案。我发现它更干净,因为我不需要 webconfig 加上 cors 配置,后者需要设置得恰到好处才能让 2 协同工作。这摆脱了 webConfig 中的 cors 东西,所以你只能将它设置在一个地方,在你 API 应用程序代码本身中。

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

只是简要总结一下 link 中的内容。

添加到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;

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

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

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

  • 首先,理解 CORS 实际上 真的 很重要。我建议你预留 15~30 分钟的时间,这会提前节省你的时间,相信我。作为, read the docs: 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)
})...;

就这些了

正如另一个 post 中某人所建议的那样,假设您没有使用 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();
});