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.
    评论

报告相同问题?

悬赏问题

  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?