【发布时间】: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