设置HTTP头


165

我正在尝试在Go Web服务器中设置标题。我正在使用gorilla/muxnet/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请求标头作为客户端发送的文档-我不确定如何设置响应标头?

Answers:


227

没关系,我知道了-我Set()Header()(doh!)上使用了方法

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

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了,这将不起作用(发送凭据时不允许使用“ *”值,这是一种常见的用例)。您必须将原点设置为请求者(有关具体方法,请参见下面的Matt Bucci回答)。
orcaman 2015年

98

上述所有答案都是错误的,因为它们无法处理OPTIONS预检请求,解决方案是覆盖多路复用器路由器的接口。请参阅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
“以上所有”……答案可以用多种方式排序,因此该短语并不表示您想要的。
Dave C

简单的CORS请求没有任何先验,这完全取决于您要提供的服务。
laike9m

不要忘记Access-Control-Allow-Credentials": "true"使用httpOnly Cookies的请求。
Federico

23

在真正需要完全公开的行为之前,请勿将“ *”用于“来源”。
维基百科所说

““ *”的值是特殊的,因为它不允许请求提供凭据,这意味着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)
    }
}

并且不要忘记将所有这些标头答复给preflight OPTIONS请求。


1
我不太了解此包装器的用法,能否举个例子说明如何使用此代码包装http句柄?我使用的是大猩猩混合体,所以我目前的使用方式是 router.HandleFunc("/user/action", user.UserAction) http.Handle("/", router) http.ListenAndServe(":8080", nil).Set("Access-Control-Allow-Origin", "*")
Matt Bucci 2014年

2
我现在我的包裹处理呼叫与像addDefaultHeaders router.HandleFunc("/user/action", addDefaultHeaders(user.UserAction)) 但因为我有大约16条路线,这是不理想的是有没有办法将其指定为在HTTP包的包装或MUX路由器层
马特·布奇

14

设置适当的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(),
))

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方法选项的预检请求,您必须在路由器中显式处理该请求,然后,如果它"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)GO服务器的Beego框架

请遵循以下几点:1)必须仅在GO服务器上启用CORS设置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或您的请求类型。


-7

我知道这是一个不同的答案,但这不是Web服务器要担心的问题吗?例如,nginx可能会有所帮助。

所述ngx_http_headers_module模块允许添加“过期”和“缓存控制”报头字段,和任意字段,到响应报头

...

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

在生产中的go服务之前添加nginx似乎是明智的。它为授权,记录和修改请求提供了更多功能。而且,它还可以控制谁可以访问您的服务,不仅如上所述,而且可以为应用程序中的特定位置指定不同的行为,如上所述。

我可以继续说明为什么要通过go api使用Web服务器,但是我认为这是另一个讨论的主题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.