我正在尝试从Angular前端向Golang后端发出发布请求,这两者都是在同一台计算机上进行的。我不断得到:
OPTIONS http://localhost:12345/anteroom 405 (Method Not Allowed)
和
Access to XMLHttpRequest at 'http://localhost:12345/anteroom' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
使用大猩猩多路复用器的Golang后端:
func main() {
router := mux.NewRouter()
router.HandleFunc("/anteroom", anteroom).Methods("POST")
log.Fatal(http.ListenAndServe(":12345", router))
}
func anteroom(res http.ResponseWriter, req *http.Request) {
res.Header().Set("Access-Control-Allow-Origin", "*")
// *Edit 17 Jan 19 12.44pm*: Billy, user268396, and Peter suggest that OPTIONS should be added. As mentioned below, I've already tried that.
res.Header().Set("Access-Control-Allow-Methods", "POST")
res.Header().Set("Content-Type", "application/json")
// Put it into a struct variable and print a bit of it.
_ = json.NewDecoder(req.Body).Decode(&member)
fmt.Println(member.ID)
}
角度前端组件:
export class AnteroomComponent implements OnInit {
public profile: string;
constructor(private http: HttpClient, private cookieService: CookieService) {}
ngOnInit() {}
// This is the relevant function.
// Triggered by a button.
sendProfile(Profile) {
let httpHeaders = new HttpHeaders({
"Content-Type": "application/json",
"Origin": "http://localhost:4200"
});
return this.http
.post("http://localhost:12345/anteroom", this.profile, {
headers: httpHeaders,
observe: "response"
})
.subscribe(
data => {
console.log("POST Request is successful ", data);
},
error => {
console.log("Error", error);
}
);
}
}
这是我尝试过的许多事情:
我读到,设置标题而不是我的标题是浏览器的工作(这对我来说没有意义,因为HttpHeaders()
的用途是什么?),所以我从Angular中删除了所有标题。
我尝试在Golang中启用CORS,如here所示:
(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
if (*req).Method == "OPTIONS" {
return
}
我尝试将Access-Control-Allow-Origin
从*
更改为Origin
,因为我读到某个地方*
阻止了cookie的发送/接收。 (丢失了链接。)也许它也阻止了其他一些MIME类型?我不知道,只是尝试。
Mozilla说:“构造函数初始化XMLHttpRequest。必须在任何其他方法调用之前调用它。”因此,我想我会这样做,但是Angular说:“ @ angular / common / http中的HttpClient为Angular应用程序提供了简化的客户端HTTP API,该API基于浏览器公开的XMLHttpRequest接口。”所以,我猜这没有必要吗?构造函数已经包含HttpClient
。
this.Profile
看起来很像JSON:{id: testid, username: "Mr Odour", age: "87"}
。但这也许是问题所在。因此,我将其放入String()
中。然后,我使用Golang的httputil.DumpRequest()
从前端转储了响应:
output, err := httputil.DumpRequest(req, true)
if err != nil {
fmt.Println("Error dumping request:", err)
return
}
fmt.Println(string(output))
也许这提供了更多的见解。它打印出:
POST /anteroom HTTP/1.1
Host: localhost:12345
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,en-GB;q=0.8
Connection: keep-alive
Content-Length: 15
Content-Type: text/plain
Dnt: 1
Origin: http://localhost:4200
Referer: http://localhost:4200/anteroom
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/71.0.3578.98 Safari/537.36
我认为这是成功的吗?我不确定。它没有说200 OK。控制台会打印“ POST请求成功”,但不会显示data
。我试图用Golang来阅读它:
body, err := ioutil.ReadAll(req.Body)
if err != nil {
fmt.Println(err)
}
fmt.Println(body)
这产生了[]
。空的。
它也显示Content-Type: text/plain
。那是问题吗?不应该是application/json
吗?但是其他人的代码按原样使用它,而没有this这样的String()
:
return this.http.post<Article>(this.url,
{
id: 100,
title: 'Java Functional Interface',
category: 'Java 8',
writer: 'Krishna'
}
);
编辑19年1月17日下午12:45 :Billy建议使用Header().Add()
代替Header().Set()
,如下所示:
res.Header().Add("Access-Control-Allow-Origin", "*")
res.Header().Add("Access-Control-Allow-Methods", "POST")
res.Header().Add("Access-Control-Allow-Methods", "OPTIONS")
res.Header().Add("Content-Type", "application/json")
所以,我做到了。也不起作用。
后端似乎还可以。我用curl贴到上面,它产生了:
Connected to localhost (::1) port 12345 (#0)
> POST /anteroom HTTP/1.1
> Host: localhost:12345
> User-Agent: curl/7.47.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 126
>
} [126 bytes data]
* upload completely sent off: 126 out of 126 bytes
< HTTP/1.1 200 OK
*Edit 17 Jan 19 12.45pm*: Methods contains OPTIONS once the header is set in backend. So, no problem with curl this way.
< Access-Control-Allow-Methods: POST
< Access-Control-Allow-Origin: *
< Date: Tue, 15 Jan 2019 19:34:32 GMT
< Content-Length: 0
它也可以打印出fmt.Println(member.ID)
,并且JSON字符串中的所有其他内容都很好。
编辑19年1月17日下午13:25 :当我发出curl -i -X OPTIONS http://localhost:12345/anteroom
时,返回:“ Access-Control-Allow-Methods:POST,OPTIONS”。因此,OPTIONS正在工作。但是前端请求保持不变,即使控制台成功记录下来,也没有明确的200 OK和JSON似乎没有通过。作为响应,MIME类型被列为“文本/纯文本”,但是对于JSON来说不是吗?如前所述,其他解决方案使用相同的格式,并且适用于它们。我现在正在解决问题的这一部分。
我是Golang,TypeScript和Angular的新手,所以我希望发布所有相关代码。这似乎是一个很普遍的问题,所以我在这里浏览了很多类似的问题,但仍然无法解决。我到底错过了什么?
答案 0 :(得分:2)
这不是您在后端进行CORS
的方式。您的后端需要侦听HTTP OPTIONS
类型的请求,然后在其中发送CORS标头。
控制流程大致为:
答案 1 :(得分:1)
@ user268396告诉您原因,我将告诉您如何。
“ Access-Control-Allow-Origin
”表示您允许向该服务器发出请求的来源。
它可以采用以下形式:
“ Access-Control-Allow-Methods
”应该为[] string(“ POST”,“ OPTION”)。
关于golang,请参阅godoc https://golang.org/pkg/net/http/#Header
type Header map[string][]string
我总是建议使用Header.Add()
而不是Header.Set()
,除非您确切地知道自己在做什么。因为标头中的每个值始终为[]字符串。
应该是
res.Header().Add("Access-Control-Allow-Origin", "*")
res.Header().Add("Access-Control-Allow-Methods", "POST")
res.Header().Add("Access-Control-Allow-Methods", "OPTION")
res.Header().Add("Content-Type", "application/json")
答案 2 :(得分:1)
问题得到了解决,这在很大程度上要感谢所有帮助我意识到我没有正确执行CORS的人。基本上,有两个问题:1.我不知道在后端正确设置标头,并且2.我没有正确格式化JSON数据。
我注意到这在Stack Overflow上是一个相当普遍的问题,可能部分是因为我们当中有些人并不真正了解请求的工作方式。因此,这里是一个摘要,摘自Mozilla的宝贵且简单编写的documentation和user268396的输入。我是新手,如果我错了,请纠正我。
通常,这很简单:当您要请求某些内容时,浏览器会根据您已有的内容设置适当的标头,然后将请求发送出去。服务器响应。
但是,假设您在后端有一个API POST端点,并且在同一个域上有一个前端(我想这是我们大多数人正在开发中,尽管可能不在生产中),并且您正在使用除application / x-www-form-urlencoded,multipart / form-data和text / plain,则有所不同。就我而言,我正在使用application / json。因此,它看起来像这样:
浏览器使用OPTIONS方法发送请求,询问允许使用哪些方法和内容类型以及其他内容。这称为预检请求。它还没有发送我的JSON对象。
服务器响应预检请求。
要使服务器响应所允许的内容,我们需要正确设置后端以允许Access-Control-Allow-Methods中的OPTIONS方法,如果像我一样,您正在使用Gorilla mux,请记住允许它在router.HandleFunc()
中也是如此。
因为我们的前端和后端在同一个域(http://localhost)上,所以最简单的方法是将Access-Control-Allow-Origin设置为“ *”。但是,我们可能不希望根据自己的需要在生产中执行此操作,因为*表示全世界和其他地区都可以发送请求。
因为我想接收JSON,所以我还将后端的Content-Type设置为application / json。这是我问题的另一半。长话短说,无论对其他人有什么用,出于某种原因,我仍然必须将JSON.stringify()
应用于原始JSON数据。然后效果很好。
就是这样。