【问题标题】:405 Method Not Allowed and "CORS header ‘Access-Control-Allow-Origin’ missing" although tcpdump says it's being sent out405 Method Not Allowed 和“CORS header ‘Access-Control-Allow-Origin’ missing”虽然 tcpdump 说它正在被发送出去
【发布时间】:2019-08-02 07:31:45
【问题描述】:

这个问题跟在one后面,所以有些文字是一样的。

当前端在提交表单时尝试将 JSON 数据 POST 到后端时,Firefox 控制台上的错误消息。:

“跨域请求被阻止:同源策略不允许读取位于https://backend_domain/anteroom 的远程资源。(原因:缺少CORS 标头'Access-Control-Allow-Origin')。”

我正在使用 systemd 单元运行 Golang 后端,并在 localhost:12345 上提供服务。 Nginx 监听 80 端口并将请求向下传递给它:

listen 80;
server_name backend_domain;

location / {
    include proxy_params;
    proxy_pass http://localhost:12345/;
}

我正在运行 Angular 前端作为构建(使用 --prod 标志构建),使用 PM2 和 angular-http-server 在端口 8080 提供服务。与后端相同,Nginx 从端口 80 执行其操作:

listen 80;
server_name frontend_domain;

location / {
    include proxy_params;
    proxy_pass http://localhost:8080/;
}

我正在使用的版本:Ubuntu 16.04、PM2 3.3.1、Angular CLI 7.3.4、angular-http-server 1.8.1。

开发者工具中 Firefox 的网络标签显示 POST 请求标头:

Host: backend_domain
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: frontend_domain/
Content-Type: text/plain
Content-Length: 111
Origin: frontend_domain
DNT: 1
Connection: keep-alive

以及响应头:

HTTP/1.1 405 Method Not Allowed
Server: nginx/1.10.3 (Ubuntu)
Date: Mon, 11 Mar 2019 21:08:24 GMT
Content-Length: 0
Connection: keep-alive
Strict-Transport-Security: max-age=31536000; includeSubDomains

当我点击提交按钮时应该去后端的实际请求是:

if (val.issues && val.age && val.gender) {
      this.profile = JSON.stringify({
        age: val.age,
        gender: val.gender,
        issues: val.issues
      });

      return this.http
        .post(environment.anteroomPOSTURL, this.profile, {
          observe: "response"
        })

Firefox 显示成功触发,JSON 显示在开发工具中 Network 的 Params 选项卡下。

这个响应告诉我,不知何故,Nginx 没有在端口 12345 处将请求向下传递到后端。否则,它会从我的 Golang 代码中显示的后端检索标头并将其传递回前端,对吧?

我了解到 CORS 是一个服务器端问题。因此,我尝试在有服务器的任何地方启用它,即在后端、Nginx 和 angular-http-server。

它在我的 Golang 代码中启用:

func anteroom(res http.ResponseWriter, req *http.Request) {
    res.Header().Set("Access-Control-Allow-Origin", "*")
    res.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
    res.Header().Set("Access-Control-Allow-Headers", "Content-Type")
    res.Header().Set("Content-Type", "application/json")
...
}

func main() {
    ...
    # Using Gorilla mux router.
    router := mux.NewRouter()
    router.HandleFunc("/anteroom", anteroom).Methods("POST, OPTIONS")
}

这成功地在开发中启用了 CORS,其中服务 Golang 只是打开其构建的二进制文件,而 Angular 使用 ng serve 提供服务。

以上内容在生产中还不够。因此,我尝试使用 angular-http-server 启用它。注意末尾的--cors 标志:

pm2 start $(which angular-http-server) --name app -- --path /PATH/TO/DIST -p 8080 --cors

我也尝试在后端和前端 Nginx 文件中启用它(改编自 here):

location / {
proxy_pass http://localhost:8080; # or 12345 if it's the back end conf
if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Content-Type';
        add_header 'Content-Type' 'application/json';
        return 204;
     }

     if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Content-Type';
        add_header 'Content-Type' 'application/json';
     }

     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Content-Type';
     }
}
}

奇怪的是,无论标头是否在 Nginx 文件中,tcpdump -vvvs 1024 -l -A src host backend_domain | grep 'Access-Control-Allow-Origin:' 都会生成:

        Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
        Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
        Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
        Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
        Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
        Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *

不知道为什么它会重复 12 次,但无论如何,后端会在前端加载的那一刻发送上述内容(这意味着 Nginx 确实成功地将请求向下传递到端口 12345,对吧?)。当我单击提交按钮提交表单时,它不会发送它们。我不知道这是正确的行为还是表明有问题。

我错过了什么?

19 年 3 月 12 日晚上 7 点 30 分更新:

如上所见并由 cmets 中的 sideshowbarker 指出,有一个“405 Method Not Allowed”响应。起初我认为这与 CORS 问题以及 Nginx 相关。为了验证它,我停止了 Nginx,并在端口 12345 上打开了我的防火墙,以便我可以直接 POST 到 Golang 后端。

为了避免同源策略的任何复杂性,我使用 cURL 从另一台机器上 POST:curl -v -X POST -H 'Content-Type: application/json' -d '{"age":"l","gender":"l","issues":"l"}' http://droplet_IP:12345/anteroom

我得到了完全相同的响应:“HTTP/1.1 405 Method Not Allowed”。

在这一点上,我最好的猜测是 Golang 后端不允许 POST,即使它在代码中明确允许,如上所示。我不知道为什么。

【问题讨论】:

  • 得到 405 的实际请求是什么?你离开了那条线(POST /anteroom HTTP/1.1?)
  • 感谢@kichik 的提醒。我在讨论请求和响应标头的地方添加了它。
  • 问题中的详细信息表明 405 错误响应实际上不包含 Access-Control-Allow-Origin 响应标头。无论如何,即使它确实包含 Access-Control-Allow-Origin,它仍然是 405 错误。所以看起来你需要解决的实际问题是找出导致 405 错误的原因并修复它。
  • @sideshowbarker 是的,感谢您指出。我认为 405 与缺少标题有关或引起。但事实并非如此。我已经更新了问题。
  • 我也面临同样的问题。我在后端使用 nodejs,所以我认为 Go 没有问题。当我在 cloudflare 和 nginx 之间打开 ssl 时,这种情况就开始发生了

标签: angular go nginx cors microservices


【解决方案1】:

问题主要在于 Golang 代码中 main() 中 Gorilla mux 的这一行:

router.HandleFunc("/anteroom", anteroom).Methods("POST, OPTIONS")

注意后面的方法:("POST, OPTIONS")。这是错误的。应该是("POST", "OPTIONS")。它们必须单独引用。

这和我们用net/http:res.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")为页面设置函数中的方法不同。在这里,它们被一起引用。

我认为可能有各种各样的问题,但这是一段非常累人的旅程,我现在只记得最后一个。

【讨论】:

    猜你喜欢
    • 2012-10-28
    • 2014-11-01
    • 2012-11-08
    • 2016-09-30
    • 2013-12-09
    • 2019-02-07
    • 2016-08-30
    • 2019-10-10
    • 1970-01-01
    相关资源
    最近更新 更多