175

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

4

8 回答 8

243

没关系,我想通了-我在(doh!)上使用了该Set()方法Header()

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

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

也许这会在某个时候帮助像我一样缺乏咖啡因的人:)

于 2012-10-10T23:46:18.393 回答
98

以上所有答案都是错误的,因为它们无法处理 OPTIONS 预检请求,解决方案是覆盖 mux 路由器的接口。请参阅AngularJS $http get request failed with custom header(在 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)
}
于 2014-07-18T06:37:52.177 回答
24

在您确实需要完全公开的行为之前,请勿使用 '*' 作为 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 请求。

于 2013-12-20T09:43:27.657 回答
17

如果您不想覆盖您的路由器(如果您没有以支持此功能的方式配置您的应用程序,或者想要逐个路由配置 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 Method OPTIONS 的预检请求,您必须在路由器中显式处理该请求,然后,如果它"Access-Control-Allow-Origin": origin从您的应用程序接收到适当的响应(或“*”),它会启动实际的要求。

我也相信您只能对标准类型的请求(即:GET)执行“*”,但对于其他请求,您必须像我上面那样明确设置来源。

于 2015-02-26T23:26:47.957 回答
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(),
))
于 2016-05-10T12:25:16.450 回答
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)
    }
}
于 2013-05-08T18:06:25.573 回答
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 或您的请求类型。

于 2016-05-02T08:42:31.203 回答
-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 服务器,但我认为这是另一个讨论的话题。

于 2015-10-04T12:59:03.593 回答