dongzhan7253 2019-01-16 22:04
浏览 707

CORS策略不允许和阻止Angular前端POST到Golang后端方法

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 * to Origin 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 into String(). Then I dumped the response from the front end with Golang's httputil.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 of Header().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?

  • 写回答

3条回答

  • dongxie7683 2019-01-16 22:33
    关注

    This is not how you do CORS on the backend. Your backend needs to listen to HTTP OPTIONS type requests and send the CORS headers there.

    The control flow is roughly:

    1. You request something in the frontend
    2. The browser determines: uh-oh, this requires CORS
    3. The browser first performs an OPTIONS request to the requested resources to negotiate CORS
    4. Depending on the result, either browser throws your frontend an error
    5. Or it continues with the actual request your frontend issued.
    6. Things work almost normally from here on out
    7. When the browser gets the result of your actual API call back it filters out things that have not been whitelisted/negotiated in steps 3 and 4.
    8. It presents that potentially censored result to the frontend as the result of the HTTP call.
    评论

报告相同问题?

悬赏问题

  • ¥15 数值计算均差系数编程
  • ¥15 redis-full-check比较 两个集群的数据出错
  • ¥15 Matlab编程问题
  • ¥15 训练的多模态特征融合模型准确度很低怎么办
  • ¥15 kylin启动报错log4j类冲突
  • ¥15 超声波模块测距控制点灯,灯的闪烁很不稳定,经过调试发现测的距离偏大
  • ¥15 import arcpy出现importing _arcgisscripting 找不到相关程序
  • ¥15 onvif+openssl,vs2022编译openssl64
  • ¥15 iOS 自定义输入法-第三方输入法
  • ¥15 很想要一个很好的答案或提示