设置HTTP标头
我正在尝试在Go Web服务器中设置一个标头。 我正在使用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请求标题的文档,就像它是一个客户端 – 我不确定如何设置响应标题?
没关系,我弄明白了 – 我在Header()
上使用了Set()
方法(doh!)
我的处理程序看起来像这样:
func saveHandler(w http.ResponseWriter, r *http.Request) { // allow cross domain AJAX requests w.Header().Set("Access-Control-Allow-Origin", "*") }
也许这将有助于某人咖啡因被剥夺为我自己有时:)
以上所有答案都是错误的,因为它们无法处理OPTIONS预检请求,解决办法是覆盖多路复用路由器的接口。 请参阅AngularJS $ http获取请求失败与自定义标题(在CORS中alllowed)
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 srServeHTTP(rw, req) }
除非你真的需要一个完全公开的行为,否则不要使用“*”作为Origin。
正如维基百科所说 :
“*”的价值是特殊的,它不允许请求提供证书,这意味着HTTPauthentication,客户端SSL证书,也不允许发送cookie。
这意味着,你会得到很多错误,特别是在Chrome中,当你尝试实现一个简单的身份validation。
这是一个更正的包装:
// 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) } }
不要忘记将所有这些标题回复到预检选项请求。
我为这种情况创build包装:
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) } }
如果你不想覆盖你的路由器 (因为我们中的许多人没有以这种方式configuration我们的应用程序,或者只是想要通过路线来configurationCORS),只需添加一个OPTIONS处理程序来处理预飞行请求。
也就是说,大猩猩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
(or“*”所有)从您的应用程序,它启动实际的请求。
我也相信你只能为标准types的请求(即:GET)做“*”,但是对于其他的你必须像上面那样明确地设置原点。
设置一个合适的golang中间件,以便您可以在任何端点上重用。
助手的types和function
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) }) } }
端点
REMEBER! 中间件以相反的顺序被应用(ExpectGET()首先被触发)
mux.Handle("/watcher/{action}/{device}",Adapt(api.SerialHandler(mux), api.EnableCORS(), api.ExpectGET(), ))
我有同样的问题,如上所述上述解决scheme是正确的,我的设置如下1)客户端Angularjs 2)用于GO服务器的Beego框架
请遵循以下几点1)CORS设置必须在GO服务器上启用2)不要在angularJS中添加任何types的头文件,除了这个
.config(['$httpProvider', function($httpProvider) { $httpProvider.defaults.useXDomain = true; delete $httpProvider.defaults.headers.common['X-Requested-With']; }])
在您的GO服务器中,在请求开始处理之前添加CORS设置,以便预检请求接收200 OK,之后OPTIONS方法将转换为GET,POST,PUT或您的请求types。
我知道这是一个不同的答案,但是这不是一个更关心的Web服务器? 例如, nginx ,可以帮助。
ngx_http_headers_module模块允许将“Expires”和“Cache-Control”头字段以及任意字段添加到响应头
... location ~ ^<REGXP MATCHING CORS ROUTES> { add_header Access-Control-Allow-Methods POST ... } ...
在生产服务前添加nginx似乎是明智之举。 它提供了更多function来授权,logging和修改请求。 此外,它还能够控制谁可以访问您的服务,而不仅仅是这样,但是您可以为应用中的特定位置指定不同的行为,如上所示。
我可以继续谈谈为什么要使用web服务器,但我认为这是另一个讨论的话题。