Как обрабатывать запросы предпросмотра CORS на сервере Go
Итак, я пишу этот бэкэнд RESTful в Go, который будет вызываться с межсайтовыми HTTP-запросами, т.е. из контента, обслуживаемого другим сайтом (на самом деле, только другим портом, но политика одного и того же происхождения запускается, поэтому здесь мы).
В этом случае пользовательский агент в некоторых случаях отправляет запросы предполетных OPTIONS, чтобы проверить, безопасно ли отправлять фактический запрос.
Мой вопрос - как лучше всего справляться с этими предполетными запросами и удовлетворять их в контексте Go. Способы, которые я задумал, не очень элегантны, и мне интересно, есть ли другой подход к этому, о котором я не думал.
Используя стандартный пакет net/http
, я могу проверить метод запроса в функции обработчика, возможно, вот так:
func AddResourceHandler(rw http.ResponseWriter, r *http.Request) {
switch r.Method {
case "OPTIONS":
// handle preflight
case "PUT":
// respond to actual request
}
}
Я также могу использовать пакет Gorilla mux
и зарегистрировать обработчик предпросмотра "OPTIONS" для каждого соответствующего URL-адреса.
r := mux.NewRouter()
r.HandleFunc("/someresource/item", AddResourceHandler).Methods("PUT")
r.HandleFunc("/someresource/item", PreflightAddResourceHandler).Methods("OPTIONS")
Возможно, ответ на этот вопрос просто: Да, это ваши основные варианты. Но я думал, что вокруг этого может быть какая-то лучшая практика, о которой я не знаю.
Ответы
Ответ 1
Один простой способ отделить свою логику и повторное использование обработчика CORS, который вы определяете, - это обернуть обработчик REST. Например, если вы используете net/http и метод Handle
, вы всегда можете сделать что-то вроде:
func corsHandler(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if (r.Method == "OPTIONS") {
//handle preflight in here
} else {
h.ServeHTTP(w,r)
}
}
}
Вы можете сделать следующее:
http.Handle("/endpoint/", corsHandler(restHandler))
Ответ 2
Мне лично сложно добавить маршруты предпросмотра для каждого пути, который получит запрос OPTIONS
, поэтому вместо этого я просто добавляю обработчик к любому методу OPTIONS
, который обрабатывает Gorilla следующим образом:
router.Methods("OPTIONS").HandlerFunc(
func(w http.ResponseWriter, r *http.Request){
myHttpLib.OptionsForBrowserPreflight(w, r)
})
Обратите внимание, что это должно произойти до сопоставления других маршрутов, потому что если у вас есть путь, подобный "/foo"
, и вы регистрируете это сначала без указания каких-либо методов для этого маршрута, тогда запрос OPTIONS на "/foo" будет работать вместо вашего кода предполета, потому что его первое совпадение.
Таким образом вы можете: (1) иметь только одну регистрацию маршрутизации для всех предварительных полетов и (2) иметь одного обработчика для повторного использования кода и применять логику/правила в одном месте для запросов OPTIONS.
Ответ 3
Эта библиотека выглядит многообещающей:
CORS - это обработчик net/http, реализующий совместное использование ресурсов Cross Origin Спецификация W3 в Голанге.
Ответ 4
gorilla/handlers также имеет красивый обработчик CORS: cors.go
Пример использования:
import (
"net/http"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/users", UserEndpoint)
r.HandleFunc("/projects", ProjectEndpoint)
// Apply the CORS middleware to our top-level router, with the defaults.
http.ListenAndServe(":8000", handlers.CORS()(r))
}
Ответ 5
Вот фрагмент кода, который работал для меня:
addCorsHeader(res)
if req.Method == "OPTIONS" {
res.WriteHeader(http.StatusOK)
return
} else {
h.APIHandler.ServeHTTP(res, req)
}
func addCorsHeader(res http.ResponseWriter) {
headers := res.Header()
headers.Add("Access-Control-Allow-Origin", "*")
headers.Add("Vary", "Origin")
headers.Add("Vary", "Access-Control-Request-Method")
headers.Add("Vary", "Access-Control-Request-Headers")
headers.Add("Access-Control-Allow-Headers", "Content-Type, Origin, Accept, token")
headers.Add("Access-Control-Allow-Methods", "GET, POST,OPTIONS")
}
Ответ 6
Ну, из моего приложения Vue.js ничего не получалось, поэтому я сделал это.
cors := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, //viper.GetString("ORIGIN_ALLOWED")
AllowedHeaders: []string{"Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token", "Authorization"},
AllowedMethods: []string{"GET", "PATCH", "POST", "PUT", "OPTIONS", "DELETE"},
Debug: true,
AllowCredentials: true,
})
cors.Handler(corsMiddle())
func corsMiddle() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
if request.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
}
})
}