我正在尝试在我的 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请求标头的文档,就好像它是客户端一样 - 我不太确定如何设置响应标头?
没关系,我想通了 - 我在
Set()
上使用了 Header()
方法(doh!)
我的处理程序现在看起来像这样:
func saveHandler(w http.ResponseWriter, r *http.Request) {
// allow cross domain AJAX requests
w.Header().Set("Access-Control-Allow-Origin", "*")
}
也许这对像我这样缺乏咖啡因的人有帮助:)
以上所有答案都是错误的,因为它们无法处理 OPTIONS 预检请求,解决方案是覆盖 mux 路由器的接口。请参阅 AngularJS $http get 请求因自定义标头失败(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)
}
请勿使用“*”作为 Origin,除非您确实需要完全公开的行为。
正如维基百科所说:
““*”的值很特殊,因为它不允许请求提供凭据, 意味着 HTTP 身份验证、客户端 SSL 证书,也不允许 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 请求。
如果您不想覆盖路由器(如果您的应用程序没有以支持此功能的方式配置,或者想要逐个路由地配置 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 方法选项发出请求,您必须在路由器中显式处理该请求,然后,如果它从您的应用程序收到适当的响应
"Access-Control-Allow-Origin": origin
(或“*”),它就会启动实际的请求。
我还相信您只能对标准类型的请求(即:GET)执行“*”,但对于其他类型的请求,您必须像我上面那样显式设置来源。
我为这个案例创建了包装器:
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)
}
}
设置合适的 golang 中间件,以便您可以在任何端点上重用。
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)
})
}
}
mux.Handle("/watcher/{action}/{device}",Adapt(api.SerialHandler(mux),
api.EnableCORS(),
api.ExpectGET(),
))
我遇到了与上述相同的问题,上面给出的解决方案是正确的,我的设置如下 1)客户端的Angularjs 2)GO服务器的Beego框架
请遵循以下几点 1) CORS设置必须仅在GO服务器上启用 2)不要在 angularJS 中添加除此之外的任何类型的标头
.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}])
在 GO 服务器中,在请求开始处理之前添加 CORS 设置,以便预检请求收到 200 OK,之后 OPTIONS 方法将转换为 GET、POST、PUT 或您的请求类型。
就我而言,我使用 React 应用程序作为“http 调用源”。我尝试使用该线程中的许多建议,但显然没有一个起作用。
然后我查看了浏览器网络开发选项卡,然后我意识到react发送了一个OPTION请求,在请求标头处有'Access-Control-Request-Headers:授权,内容类型,用户代理'
此时,我在响应头“Access-Control-Allow-Headers”中添加了“user-agent”,然后一切正常。 “最终”代码是:
res.Header().Set("Access-Control-Allow-Headers", "authorization,content-type,user-agent")
我知道这是一个不同的答案,但这不是网络服务器更关心的问题吗?例如,nginx,可以提供帮助。
ngx_http_headers_module模块允许将“Expires”和“Cache-Control”标头字段以及任意字段添加到响应标头
...
location ~ ^<REGXP MATCHING CORS ROUTES> {
add_header Access-Control-Allow-Methods POST
...
}
...
在生产中的 go 服务前面添加 nginx 似乎是明智的。它提供了更多用于授权、记录和修改请求的功能。此外,它还提供了控制谁有权访问您的服务的能力,不仅如此,还可以为应用程序中的特定位置指定不同的行为,如上所示。
我可以继续讨论为什么要在 go api 中使用 Web 服务器,但我认为这是另一个讨论的主题。