Налаштування заголовків HTTP


165

Я намагаюся встановити заголовок на своєму веб-сервері Go. Я використовую gorilla/muxі net/httpпакети.

Я хотів би встановити Access-Control-Allow-Origin: *дозвіл міждоменного AJAX.

Ось мій код Go:

func saveHandler(w http.ResponseWriter, r *http.Request) {
// do some stuff with the request data
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/save", saveHandler)
    http.Handle("/", r)
    http.ListenAndServe(":"+port, nil)
}

У net/httpпакеті є документація, що описує надсилання заголовків запитів http, ніби це клієнт - я не точно знаю, як встановити заголовки відповідей?

Відповіді:


227

Неважливо, я зрозумів це - я використав Set()метод Header()(до!)

Зараз мій обробник виглядає так:

func saveHandler(w http.ResponseWriter, r *http.Request) {
    // allow cross domain AJAX requests
    w.Header().Set("Access-Control-Allow-Origin", "*")
}

Можливо, це допоможе комусь, бо кофеїн позбавлений колись я :)


2
У мене те саме питання, можливо, також буде корисно додати: w.Header().Add("Access-Control-Allow-Methods", "PUT") w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
Рей

1
Це не спрацює, якщо набір клієнтів AJAX withCredentials:true(значення "*" заборонено при надсиланні облікових даних, що є загальним випадком використання). Ви повинні встановити походження запитувача (див. Відповідь Метта Баччі нижче).
orcaman

98

Усі вищезазначені відповіді помилкові, оскільки вони не справляються із запитом перед польотом OPTIONS, рішення - перекрити інтерфейс маршрутизатора mux. Перегляньте запит на отримання AngularJS $ http з помилковим запитом із користувацьким заголовком (все включено у CORS)

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/save", saveHandler)
    http.Handle("/", &MyServer{r})
    http.ListenAndServe(":8080", nil);

}

type MyServer struct {
    r *mux.Router
}

func (s *MyServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
    if origin := req.Header.Get("Origin"); origin != "" {
        rw.Header().Set("Access-Control-Allow-Origin", origin)
        rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
        rw.Header().Set("Access-Control-Allow-Headers",
            "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
    }
    // Stop here if its Preflighted OPTIONS request
    if req.Method == "OPTIONS" {
        return
    }
    // Lets Gorilla work
    s.r.ServeHTTP(rw, req)
}

19
"Все вищезазначене" ... відповіді можна сортувати багатьма способами, тому ця фраза не означає, що ви хочете.
Дейв C

Прості запити CORS не мають попереднього польоту, все залежить від того, що ви намагаєтеся обслуговувати.
laike9m

Не забувайте Access-Control-Allow-Credentials": "true"про запити з httpOnly Cookies.
Федеріко

23

Не використовуйте "*" для Origin, поки вам справді не потрібна повністю публічна поведінка.
Як говорить Вікіпедія :

"Значення" * "особливе тим, що воно не дозволяє запитам надати облікові дані, що означає аутентифікацію HTTP, сертифікати на стороні клієнта, а також не дозволяє надсилати файли cookie."

Це означає, що ви отримаєте багато помилок, особливо в Chrome, коли спробуєте реалізувати, наприклад, просту аутентифікацію.

Ось виправлена ​​обгортка:

// Code has not been tested.
func addDefaultHeaders(fn http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if origin := r.Header.Get("Origin"); origin != "" {
            w.Header().Set("Access-Control-Allow-Origin", origin)
        }
        w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token")
        w.Header().Set("Access-Control-Allow-Credentials", "true")
        fn(w, r)
    }
}

І не забудьте відповісти на всі ці заголовки на запит OPTIONS перед передпольотом.


1
Я не зовсім розумію використання цієї обгортки, чи можете ви навести приклад того, як ви обмотаєте http-ручку цим кодом? Я використовую gorilla mux, тому моє поточне використання router.HandleFunc("/user/action", user.UserAction) http.Handle("/", router) http.ListenAndServe(":8080", nil).Set("Access-Control-Allow-Origin", "*")
Метт Баччі

2
Зараз я переношу свої дзвінки на обробку за допомогою addDefaultHeaders, router.HandleFunc("/user/action", addDefaultHeaders(user.UserAction)) однак, оскільки у мене є близько 16 маршрутів, це не ідеально, чи є якийсь спосіб вказати його як обгортку на http-пакеті або мукс-маршрутизаторі
Метт Баччі

14

Встановіть належне програмне забезпечення для голанг, щоб ви могли повторно використовувати його в будь-якій кінцевій точці.

Тип та функції помічника

type Adapter func(http.Handler) http.Handler
// Adapt h with all specified adapters.
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
    for _, adapter := range adapters {
        h = adapter(h)
    }
    return h
}

Фактичне проміжне програмне забезпечення

func EnableCORS() Adapter {
    return func(h http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

            if origin := r.Header.Get("Origin"); origin != "" {
                w.Header().Set("Access-Control-Allow-Origin", origin)
                w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
                w.Header().Set("Access-Control-Allow-Headers",
                    "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
            }
            // Stop here if its Preflighted OPTIONS request
            if r.Method == "OPTIONS" {
                return
            }
            h.ServeHTTP(w, r)
        })
    }
}

Кінцева точка

ПАМ’ЯТАЙТЕ! Посередні програми застосовуються у зворотному порядку (ExpectGET () спочатку отримує пожежі)

mux.Handle("/watcher/{action}/{device}",Adapt(api.SerialHandler(mux),
    api.EnableCORS(),
    api.ExpectGET(),
))

14

Якщо ви не хочете змінювати маршрутизатор (якщо у вас немає програми, налаштованої таким чином, або ви хочете налаштувати CORS на маршруті за маршрутом), додайте обробник OPTIONS для обробки запиту перед польотом .

Тобто, з Gorilla Mux ваші маршрути виглядали б так:

accounts := router.Path("/accounts").Subrouter()
accounts.Methods("POST").Handler(AccountsCreate)
accounts.Methods("OPTIONS").Handler(AccountsCreatePreFlight)

Зауважте вище, що крім нашого обробника POST, ми визначаємо конкретний обробник методу OPTIONS .

А потім, щоб фактично обробити метод передпольоту OPTIONS, ви можете визначити AccountsCreatePreFlight так:

// Check the origin is valid.
origin := r.Header.Get("Origin")
validOrigin, err := validateOrigin(origin)
if err != nil {
    return err
}

// If it is, allow CORS.
if validOrigin {
    w.Header().Set("Access-Control-Allow-Origin", origin)
    w.Header().Set("Access-Control-Allow-Methods", "POST")
    w.Header().Set("Access-Control-Allow-Headers",
        "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}

Що насправді змусило все це натиснути для мене (окрім того, що насправді розуміє, як працює CORS), це те, що метод HTTP запиту перед польотом відрізняється від методу HTTP фактичного запиту. Щоб ініціювати CORS, браузер надсилає запит на попередній політ за допомогою методу HTTP OPTIONS, з яким вам потрібно явно обробити свій маршрутизатор, а потім, якщо він отримає відповідну відповідь "Access-Control-Allow-Origin": origin(або "*" для всіх) від вашої програми, він ініціює фактичну запит.

Я також вважаю, що ви можете робити "*" тільки для стандартних типів запитів (тобто: GET), але для інших вам потрібно буде чітко встановити походження, як я, як це я робив вище.


12

Я створюю обгортку для цього випадку:

func addDefaultHeaders(fn http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        fn(w, r)
    }
}

1

У мене була та сама проблема, що описана вище, рішення, наведені вище, є правильними, налаштування у мене таке: 1) Angularjs для клієнта 2) Рамка Beego для GO сервера

Будь ласка, дотримуйтесь цих пунктів 1) Налаштування CORS повинні бути включені лише на сервері GO 2) НЕ додайте заголовки в кутовий JS, крім цього

.config(['$httpProvider', function($httpProvider) {
        $httpProvider.defaults.useXDomain = true;
        delete $httpProvider.defaults.headers.common['X-Requested-With'];
    }])

На вашому сервері GO додайте параметри CORS перед тим, як запит почне оброблятися, щоб запит перед польотом отримав 200 ОК, після чого метод OPTIONS буде перетворений на GET, POST, PUT або будь-який тип вашого запиту.


-7

Я знаю, що це є різним поворотом у відповіді, але чи не це більше хвилює веб-сервер? Наприклад, nginx , може допомогти.

Модуль ngx_http_headers_module дозволяє додавати в заголовок відповіді поля заголовка "Закінчується" та "Кеш-контроль" та довільні поля.

...

location ~ ^<REGXP MATCHING CORS ROUTES> {
    add_header Access-Control-Allow-Methods POST
    ...
}
...

Додавання nginx перед службою Go у виробництві видається розумним. Він надає набагато більше функцій для авторизації, реєстрації та зміни запитів. Крім того, це дає змогу контролювати, хто має доступ до вашої послуги, і не тільки це, але можна вказати різну поведінку для певних місць у вашому додатку, як показано вище.

Я можу продовжити те, чому слід використовувати веб-сервер з вашим go api, але я думаю, що це тема для іншого обговорення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.