设置HTTP头部

huangapple go评论81阅读模式
英文:

Setting HTTP headers

问题

我正在尝试在我的Go Web服务器中设置一个头部。我正在使用gorilla/muxnet/http包。

我想设置Access-Control-Allow-Origin: *以允许跨域AJAX。

这是我的Go代码:

func saveHandler(w http.ResponseWriter, r *http.Request) {
// 处理请求数据
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/save", saveHandler)
    http.Handle("/", r)
    http.ListenAndServe(":"+port, nil)
}

net/http包有关于发送HTTP请求头的文档,就像它是一个客户端一样 - 我不太确定如何设置响应头部?

英文:

I'm trying to set a header in my Go web server. I'm using gorilla/mux and net/http packages.

I'd like to set Access-Control-Allow-Origin: * to allow cross domain AJAX.

Here's my Go code:

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

The net/http package has documentation describing sending http request headers as if it were a client - I'm not exactly sure how to set response headers?

答案1

得分: 251

不要紧,我弄清楚了 - 我在 Header() 上使用了 Set() 方法(噢!)

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

func saveHandler(w http.ResponseWriter, r *http.Request) {
    // 允许跨域 AJAX 请求
    w.Header().Set("Access-Control-Allow-Origin", "*")
}

也许这对像我一样缺乏咖啡因的人有所帮助 设置HTTP头部

英文:

Never mind, I figured it out - I used the Set() method on Header() (doh!)

My handler looks like this now:

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

Maybe this will help someone as caffeine deprived as myself sometime 设置HTTP头部

答案2

得分: 98

所有上述答案都是错误的,因为它们未能处理OPTIONS预检请求,解决方案是覆盖mux路由器的接口。请参阅https://stackoverflow.com/questions/22452804/angularjs-http-get-request-failed-with-custom-header-alllowed-in-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")
    }
    // 如果是预检的OPTIONS请求,则在此停止
    if req.Method == "OPTIONS" {
        return
    }
    // 让Gorilla处理
    s.r.ServeHTTP(rw, req)
}
英文:

All of the above answers are wrong because they fail to handle the OPTIONS preflight request, the solution is to override the mux router's interface. See https://stackoverflow.com/questions/22452804/angularjs-http-get-request-failed-with-custom-header-alllowed-in-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)
}

答案3

得分: 22

不要在Origin中使用'*',除非你真的需要完全公开的行为。正如Wikipedia所说

“'*'的值是特殊的,它不允许请求提供凭据,也就是HTTP身份验证、客户端SSL证书,也不允许发送cookie。”

这意味着,当你尝试实现简单的身份验证时,你会遇到很多错误,特别是在Chrome中。

这是一个修正后的包装器:

// 代码未经测试。
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。

英文:

Do not use '*' for Origin, until You really need a completely public behavior.
As Wikipedia says:

> "The value of "*" is special in that it does not allow requests to supply credentials,
> meaning HTTP authentication, client-side SSL certificates, nor does it allow cookies
> to be sent."

That means, you'll get a lot of errors, especially in Chrome when you'll try to implement for example a simple authentication.

Here is a corrected wrapper:

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

And don't forget to reply all these headers to the preflight OPTIONS request.

答案4

得分: 18

如果您不想覆盖您的路由器(如果您的应用程序没有以支持此功能的方式配置,或者想要逐个路由配置CORS),请添加一个OPTIONS处理程序来处理预检请求。

例如,使用Gorilla Mux,您的路由将如下所示:

accounts := router.Path("/accounts").Subrouter()
accounts.Methods("POST").Handler(AccountsCreate)
accounts.Methods("OPTIONS").Handler(AccountsCreatePreFlight)

请注意,除了我们的POST处理程序之外,我们还定义了一个特定的OPTIONS方法处理程序。

然后,要实际处理OPTIONS预检方法,您可以像这样定义AccountsCreatePreFlight:

// 检查来源是否有效。
origin := r.Header.Get("Origin")
validOrigin, err := validateOrigin(origin)
if err != nil {
return err
}

// 如果有效,则允许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"(或者对于所有请求为"*"),它会启动实际请求。

我还相信,您只能对标准类型的请求(例如:GET)使用"*",但对于其他类型的请求,您必须像我上面所做的那样显式设置来源。

英文:

If you don't want to override your router (if you don't have your app configured in a way that supports this, or want to configure CORS on a route by route basis), add an OPTIONS handler to handle the pre flight request.

Ie, with Gorilla Mux your routes would look like:

accounts := router.Path("/accounts").Subrouter()
accounts.Methods("POST").Handler(AccountsCreate)
accounts.Methods("OPTIONS").Handler(AccountsCreatePreFlight)

Note above that in addition to our POST handler, we're defining a specific OPTIONS method handler.

And then to actual handle the OPTIONS preflight method, you could define AccountsCreatePreFlight like so:

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

What really made this all click for me (in addition to actually understanding how CORS works) is that the HTTP Method of a preflight request is different from the HTTP Method of the actual request. To initiate CORS, the browser sends a preflight request with HTTP Method OPTIONS, which you have to handle explicitly in your router, and then, if it receives the appropriate response "Access-Control-Allow-Origin": origin (or "*" for all) from your application, it initiates the actual request.

I also believe that you can only do "*" for standard types of requests (ie: GET), but for others you'll have to explicitly set the origin like I do above.

答案5

得分: 13

设置一个适当的golang中间件,以便您可以在任何端点上重用。

Helper Type and Function

type Adapter func(http.Handler) http.Handler
// 使用所有指定的适配器来适应h。
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")
			}
			// 如果是预检请求,则在此停止
			if r.Method == "OPTIONS" {
				return
			}
			h.ServeHTTP(w, r)
		})
	}
}

端点

REMEBER!中间件以相反的顺序应用(ExpectGET()首先触发)

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

Set a proper golang middleware, so you can reuse on any endpoint.

Helper Type and 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
}

##Actual middleware

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

##Endpoint

REMEBER! Middlewares get applyed on reverse order( ExpectGET() gets fires first)

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

答案6

得分: 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)
	}
}
英文:

I create wrapper for this case:

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

答案7

得分: 1

我遇到了与上述描述相同的问题,上面给出的解决方案是正确的,我所使用的设置如下:
1)Angularjs用于客户端
2)Beego框架用于GO服务器

请按照以下要点进行操作:
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或其他请求类型。

英文:

I had the same issue as described above the solutions given above are correct, the set up I have is as follows

  1. Angularjs for the Client
  2. Beego framework for GO server

Please following these points

  1. CORS settings must be enabled only on GO server

  2. Do NOT add any type of headers in angularJS except for this

    .config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.useXDomain = true;
    delete $httpProvider.defaults.headers.common['X-Requested-With'];
    }])

In you GO server add the CORS settings before the request starts to get processed so that the preflight request receives a 200 OK after which the the OPTIONS method will get converted to GET,POST,PUT or what ever is your request type.

答案8

得分: -7

我知道这是对答案的不同解释,但这更多地是对Web服务器的关注吗?例如,nginx可以帮助。

ngx_http_headers_module模块允许向响应头添加“Expires”和“Cache-Control”头字段以及任意字段。

...

location ~ ^ {
add_header Access-Control-Allow-Methods POST
...
}
...

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

我可以继续讨论为什么要在go api中使用Web服务器,但我认为这是另一个讨论的话题。

英文:

I know this is a different twist on the answer, but isn't this more of a concern for a web server? For example, nginx, could help.

> The ngx_http_headers_module module allows adding the “Expires” and “Cache-Control” header fields, and arbitrary fields, to a response header

...

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

Adding nginx in front of your go service in production seems wise. It provides a lot more feature for authorizing, logging,and modifying requests. Also, it gives the ability to control who has access to your service and not only that but one can specify different behavior for specific locations in your app, as demonstrated above.

I could go on about why to use a web server with your go api, but I think that's a topic for another discussion.

huangapple
  • 本文由 发表于 2012年10月11日 07:39:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/12830095.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定