设置 HTTP 标头

问题描述 投票:0回答:9

我正在尝试在我的 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请求标头的文档,就好像它是客户端一样 - 我不太确定如何设置响应标头?

http go cors http-headers
9个回答
253
投票

没关系,我想通了 - 我在

Set()
上使用了
Header()
方法(doh!)

我的处理程序现在看起来像这样:

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

也许这对像我这样缺乏咖啡因的人有帮助:)


99
投票

以上所有答案都是错误的,因为它们无法处理 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)
}

22
投票

请勿使用“*”作为 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 请求。


18
投票

如果您不想覆盖路由器(如果您的应用程序没有以支持此功能的方式配置,或者想要逐个路由地配置 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)执行“*”,但对于其他类型的请求,您必须像我上面那样显式设置来源。


13
投票

我为这个案例创建了包装器:

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)
    }
}

13
投票

设置合适的 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)
        })
    }
}

端点

记住!中间件按相反顺序应用( ExpectGET() 首先触发)

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

1
投票

我遇到了与上述相同的问题,上面给出的解决方案是正确的,我的设置如下 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 或您的请求类型。


0
投票

就我而言,我使用 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")


-7
投票

我知道这是一个不同的答案,但这不是网络服务器更关心的问题吗?例如,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 服务器,但我认为这是另一个讨论的主题。

© www.soinside.com 2019 - 2024. All rights reserved.