I'm trying to make a post request from my Angular front end to my Golang back end, both served from the same machine. I keep getting:
OPTIONS http://localhost:12345/anteroom 405 (Method Not Allowed)
and
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 back end, using Gorilla mux router:
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)
}
Angular front end component:
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);
}
);
}
}
Here're some of the many things I tried:
I read that it's the browser's job to set headers, not mine (which doesn't make sense to me because then what is
HttpHeaders()
for?), so I removed all the headers from Angular.-
I tried enabling CORS in Golang as shown here:
(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") if (*req).Method == "OPTIONS" { return }
I tried changing
Access-Control-Allow-Origin
from*
toOrigin
because I read somewhere that*
prevents cookies from being sent/received. (Lost the link.) Maybe it prevents some other MIME types too? I don't know, just trying.Mozilla says "The constructor initializes an XMLHttpRequest. It must be called before any other method calls." So I thought I'd do that but then Angular says "The HttpClient in @angular/common/http offers a simplified client HTTP API for Angular applications that rests on the XMLHttpRequest interface exposed by browsers." So, I guess that's not necessary? Constructor's already got
HttpClient
in it.-
this.Profile
seems pretty standard as JSON:{id: testid, username: "Mr Odour", age: "87"}
. But maybe it's the problem. So I put it intoString()
. Then I dumped the response from the front end with Golang'shttputil.DumpRequest()
:output, err := httputil.DumpRequest(req, true) if err != nil { fmt.Println("Error dumping request:", err) return } fmt.Println(string(output))
This provided a bit more insight, maybe. It printed out:
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
I think it came through? I'm not sure. It doesn't say 200 OK. Console does print "POST Request is successful ", but without the data
. I tried to read it with this in Golang:
body, err := ioutil.ReadAll(req.Body)
if err != nil {
fmt.Println(err)
}
fmt.Println(body)
This produced []
. Empty.
It also says Content-Type: text/plain
. Is that the problem? Shouldn't it be application/json
? But other people's code uses it the way it is, without String()
, like this:
return this.http.post<Article>(this.url,
{
id: 100,
title: 'Java Functional Interface',
category: 'Java 8',
writer: 'Krishna'
}
);
-
Edit 17 Jan 19 12.45pm: Billy suggests to use
Header().Add()
instead ofHeader().Set()
like so: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")
So, I did. Didn't work either.
The back end seems ok. I posted to it with curl and it produces:
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
It also printed out fmt.Println(member.ID)
and everything else from the JSON string just fine.
Edit 17 Jan 19 13.25pm: When I issue curl -i -X OPTIONS http://localhost:12345/anteroom
, this comes back: "Access-Control-Allow-Methods: POST, OPTIONS". So OPTIONS is working. But front end request remains the same, no explicit 200 OK and JSON doesn't seem to go through even though console logs it as a success. The MIME type is listed as "text/plain" in response, but isn't that ok for JSON? As mentioned, other solutions use the same format and it works for them. I'm working on this part of the problem now.
I'm new to Golang, TypeScript, and Angular, so I hope I'm posting all the relevant code. It seems like a popular problem so I looked through quite a handful of similar questions on here but still can't figure it out. What on earth have I missed?