Угловой внешний интерфейс POST to Golang не разрешен и заблокирован политикой CORS - PullRequest
0 голосов
/ 17 января 2019

Я пытаюсь сделать почтовый запрос от моего внешнего интерфейса 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 back end с использованием мультиплексора Gorilla:

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.

  • Я попытался включить CORS на Голанге, как показано здесь :

    (*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 говорит: «HttpClient в @ angular / common / http предлагает упрощенный клиентский HTTP API для приложений Angular, который опирается на интерфейс XMLHttpRequest, предоставляемый браузерами». Итак, я думаю, что это не обязательно? В конструкторе уже есть HttpClient.

  • this.Profile выглядит довольно стандартно, как JSON: {id: testid, username: "Mr Odour", age: "87"}. Но, возможно, это проблема. Поэтому я положил его в String(). Затем я выкинул ответ из внешнего интерфейса с 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 ОК. Консоль выводит «Запрос POST успешен», но без data. Я попытался прочитать это на Голанге:

body, err := ioutil.ReadAll(req.Body)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(body)

Это произвело []. Пустой.

Там также написано Content-Type: text/plain. Это проблема? Разве это не должно быть application/json? Но код других людей использует его таким, каким он есть, без String(), как this :

return this.http.post<Article>(this.url,
     {
    id: 100, 
    title: 'Java Functional Interface', 
    category: 'Java 8', 
    writer: 'Krishna'
     }
  );
  • Редактировать 17 января 19 12.45 вечера : Билли предлагает использовать 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 просто отлично.

Редактировать 17 января 19 13.25 : Когда я выпускаю curl -i -X OPTIONS http://localhost:12345/anteroom, возвращается сообщение: «Access-Control-Allow-Methods: POST, OPTIONS». Так что ВАРИАНТЫ РАБОТАЮТ. Но запрос переднего плана остался прежним, никаких явных 200 OK и JSON, похоже, не проходят, хотя консоль регистрирует его как успешный. MIME-тип указан в ответе как «text / plain», но разве это не нормально для JSON? Как уже упоминалось, другие решения используют тот же формат, и он работает для них. Сейчас я работаю над этой частью проблемы.

Я новичок в Golang, TypeScript и Angular, поэтому я надеюсь, что выложу весь соответствующий код. Это кажется популярной проблемой, поэтому я просмотрел довольно много подобных вопросов здесь, но все еще не могу понять это. Что я пропустил?

Ответы [ 3 ]

0 голосов
/ 17 января 2019

@ user268396 сказал вам, почему, и я расскажу вам, как.

"Access-Control-Allow-Origin" означает источник, которому вы разрешили сделать запрос на этот сервер.

Может быть в таких формах, как:

"Access-Control-Allow-Methods" должно быть [] string ("POST", "OPTION"), если вы хотите отправить данные из CORS.

Когда дело доходит до Голанга, см. Годок 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")
0 голосов
/ 17 января 2019

Проблема решена, во многом благодаря всем, кто помог мне понять, что я не делаю CORS должным образом. По сути, было две проблемы: 1. Я не знал, как правильно установить заголовки в бэкэнде, и 2. Я не форматировал свои данные JSON должным образом.

Я заметил, что это довольно популярная проблема с переполнением стека, возможно, отчасти потому, что некоторые из нас не совсем понимают, как работают запросы. Итак, вот резюме, взятое из бесценной и просто написанной документации Mozilla и ввода user268396. Я новичок в этом, поэтому, пожалуйста, поправьте меня, если я ошибаюсь.

Обычно это просто: когда вы хотите что-то запросить, браузер устанавливает соответствующие заголовки на основе того, что у вас уже есть, и отправляет запрос. Сервер отвечает.

Но, при условии, что у вас есть конечная точка API POST на вашем внутреннем сервере и внешний интерфейс в том же домене (я полагаю, что большинство из нас находится в разработке, хотя, возможно, не в производстве), и вы работаете с чем-то другим, кроме application / x-www-form-urlencoded, multipart / form-data и text / plain, тогда это немного по-другому. В моем случае я работаю с приложением / JSON. Итак, это выглядит так:

  1. Браузер отправляет запрос методом OPTIONS, спрашивая, какие методы, типы содержимого и другие вещи разрешены. Это называется предполетным запросом. Он еще не отправил мой объект JSON.

  2. Сервер отвечает на предварительный запрос.

Чтобы сервер отвечал разрешенными данными, нам нужно правильно настроить серверную часть, чтобы разрешить метод OPTIONS в Access-Control-Allow-Methods, и если вы, как и я, используете Gorilla mux, не забудьте разрешить его в router.HandleFunc() тоже.

Поскольку наши передние и внутренние части находятся в одном домене (http://localhost),, самое простое, что нужно сделать, это установить Access-Control-Allow-Origin в значение "*". Однако мы, вероятно, не хотим делайте это на производстве в зависимости от наших потребностей, потому что * означает, что весь мир и за его пределами могут отправлять запросы.

Поскольку я хочу получать JSON, я также установил Content-Type в бэкэнде на application / json. Это была другая половина моей проблемы. Короче говоря, независимо от того, что работало для других людей, по какой-то причине мне все же пришлось применить JSON.stringify() к моим необработанным данным JSON. Тогда это сработало отлично.

  1. Как только браузер получает обратно разрешенные опции, он отфильтровывает запрещенные материалы и отправляет запрос только с соответствующими данными. В моем случае это будет объект JSON.

И это все.

0 голосов
/ 17 января 2019

Это не так, как вы делаете CORS на сервере.Ваш бэкэнд должен прослушивать запросы типа HTTP OPTIONS и отправлять туда заголовки CORS.

Поток управления примерно такой:

  1. Вы запрашиваете что-то во внешнем интерфейсе
  2. Браузер определяет: э-э, для этого требуется CORS
  3. Браузер сначала выполняет запрос OPTIONS к запрашиваемым ресурсам для согласования CORS
  4. В зависимости от результата любой браузер выбрасывает ваш веб-интерфейс.ошибка
  5. Или это продолжается с фактического запроса, выданного вашим веб-интерфейсом.
  6. С этого момента все работает почти нормально
  7. Когда браузер получает результат вашего фактического обратного вызова APIон отфильтровывает вещи, которые не были внесены в белый список / согласован в шагах 3 и 4.
  8. Он представляет потенциально подвергнутый цензуре результат для внешнего интерфейса в результате вызова HTTP.
...