为Go中的多个处理程序设置HTTP头

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

Set http headers for multiple handlers in go

问题

我正在尝试为多个处理程序设置HTTP头。我最初的想法是创建一个自定义的写函数,在写入响应之前设置头部,就像下面的代码示例一样。

然而,当我将http.ResponseWriter的指针传递给我的函数并尝试从函数中访问它时,它告诉我“类型*http.ResponseWriter没有Header方法”。

设置多个处理程序的头部的最佳方法是什么,为什么指针不按照我想要的方式工作?

func HelloServer(w http.ResponseWriter, req *http.Request) {
    type Message struct {
        Name string
        Body string
        Time int64
    }

    m := Message{"Alice", "Hello", 1294706395881547000}

    b, _ := json.Marshal(m)
    WriteJSON(&w, b)
}

func WriteJSON(wr *http.ResponseWriter, rawJSON []byte) {
    (*wr).Header().Set("Content-Type", "application/json")

    io.WriteString(*wr, string(rawJSON))
}

func main() {
    http.HandleFunc("/json", HelloServer)

    err := http.ListenAndServe(":9000", nil)
    if err != nil {
        log.Fatal("ListenAndServer: ", err)
    }
}
英文:

I'm trying to set an http header for multiple handlers. My first thought was to make a custom write function that would set the header before writing the response like the code sample at the bottom.

However, when I pass a pointer to the http.ResponseWriter and try to access it from my function it tells me that "type *http.ResponseWriter has no Header method".

What is the best way to set headers for multiple handlers, and also why isn't the pointer working the way I want it to?

<!-- language: go -->

func HelloServer(w http.ResponseWriter, req *http.Request) {
    type Message struct {
	    Name string
	    Body string
	    Time int64
    }

    m := Message{&quot;Alice&quot;, &quot;Hello&quot;, 1294706395881547000}

    b, _ := json.Marshal(m)
    WriteJSON(&amp;w, b)
}

func WriteJSON(wr *http.ResponseWriter, rawJSON []byte) {
    *wr.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)

    io.WriteString(*wr, string(rawJSON))
}

func main() {
    http.HandleFunc(&quot;/json&quot;, HelloServer)

    err := http.ListenAndServe(&quot;:9000&quot;, nil)
    if err != nil {
	log.Fatal(&quot;ListenAndServer: &quot;, err)
    }
}

答案1

得分: 4

我不确定关于多个处理程序的事情,但我知道你写的代码为什么失败。关键在于这一行代码:

*wr.Header().Set("Content-Type", "application/json")

由于运算符优先级的原因,它被解释为:

*(wr.Header().Set("Content-Type", "application/json"))

由于wr的类型是*http.ResponseWriter,它是一个指向接口的指针,而不是接口本身,所以这样是行不通的。我猜你知道这一点,这就是为什么你使用了*wr。我猜你想向编译器暗示的是:

(*wr).Header().Set("Content-Type", "application/json")

如果我没弄错的话,这样应该可以编译并正常工作。

英文:

I'm not sure about the multiple handlers thing, but I do know why the code you wrote is failing. The key is that the line:

*wr.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)

is being interpreted, because of operator precedence, as:

*(wr.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;))

Since wr has the type *http.ResponseWriter, which is a pointer <i>to</i> and interface, rather than the interface itself, this won't work. I assume that you knew that, which is why you did *wr. I assume what you meant to imply to the compiler is:

(*wr).Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)

If I'm not mistaken, that should compile and behave properly.

答案2

得分: 3

你不需要使用*wr,因为它已经引用了一个指针。

wr.Header().Set("Content-Type", "application/json")应该就足够了。

如果你想为每个请求设置“全局”头部,你可以创建一个满足http.HandleFunc的函数(go.auth有一个很好的例子),然后像这样包装你的处理程序:

http.HandleFunc("/hello", Defaults(helloHandler))

还可以查看net/http文档,其中有更多的例子

英文:

You don't need to use *wr as it already references a pointer.

wr.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;) should be sufficient.

If you want to set "global" headers for every request, you can create a function that satisfies http.HandleFunc (go.auth has a good example) and then wrap your handlers like so:

http.HandleFunc(&quot;/hello&quot;, Defaults(helloHandler))

Also take a look at the net/http documentation, which has further examples.

答案3

得分: 0

我用一个错误处理程序包装我的处理程序,该处理程序调用我的AddSafeHeader函数。

我基于http://golang.org/doc/articles/error_handling.html进行了修改,因此它不使用ServeHTTP,因此可以与appstats一起使用:

http.Handle("/", appstats.NewHandler(util.ErrorHandler(rootHandler)))

这里:

package httputil

import (
"appengine"
"net/http"
"html/template"
)

func AddSafeHeaders(w http.ResponseWriter) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
w.Header().Set("Strict-Transport-Security", "max-age=2592000; includeSubDomains")
}

// 重定向到固定的URL
type redirectHandler struct {
url string
code int
}

func (rh *redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Redirect(w, r, rh.url, rh.code)
}

func Redirect(w http.ResponseWriter, r *http.Request, urlStr string, code int) {
AddSafeHeaders(w)
http.Redirect(w, r, urlStr, code)
}

// RedirectHandler返回一个请求处理程序,该处理程序使用给定的URL和状态码将其接收到的每个请求重定向。
func RedirectHandler(url string, code int) http.Handler {
return &redirectHandler{url, code}
}

func ErrorHandler(fn func(appengine.Context, http.ResponseWriter, *http.Request)) func(appengine.Context, http.ResponseWriter, *http.Request) {
return func(c appengine.Context, w http.ResponseWriter, r *http.Request) {
defer func() {
if err, ok := recover().(error); ok {
c.Errorf("%v", err)
w.WriteHeader(http.StatusInternalServerError)
errorTemplate.Execute(w, err)
}
}()
AddSafeHeaders(w)
fn(c, w, r)
}
}

// 如果err非nil,则Check中止当前执行。
func Check(err error) {
if err != nil {
panic(err)
}
}

var errorTemplate = template.Must(template.New("error").Parse(errorTemplateHTML))

const errorTemplateHTML = `



XXX

An error occurred:

{{.}}



`

英文:

I wrap my handlers with an error handler
which calls my AddSafeHeader function.

I based it on http://golang.org/doc/articles/error_handling.html
but it doesn't use ServeHTTP so it works with appstats:

http.Handle(&quot;/&quot;, appstats.NewHandler(util.ErrorHandler(rootHandler)))

Here:

package httputil

import (
  &quot;appengine&quot;
  &quot;net/http&quot;
  &quot;html/template&quot;
)

func AddSafeHeaders(w http.ResponseWriter) {
  w.Header().Set(&quot;X-Content-Type-Options&quot;, &quot;nosniff&quot;)
  w.Header().Set(&quot;X-XSS-Protection&quot;, &quot;1; mode=block&quot;)
  w.Header().Set(&quot;X-Frame-Options&quot;, &quot;SAMEORIGIN&quot;)
  w.Header().Set(&quot;Strict-Transport-Security&quot;, &quot;max-age=2592000; includeSubDomains&quot;)
}

// Redirect to a fixed URL
type redirectHandler struct {
  url  string
  code int
}

func (rh *redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  Redirect(w, r, rh.url, rh.code)
}

func Redirect(w http.ResponseWriter, r *http.Request, urlStr string, code int) {
  AddSafeHeaders(w)
  http.Redirect(w, r, urlStr, code)
}

// RedirectHandler returns a request handler that redirects
// each request it receives to the given url using the given
// status code.
func RedirectHandler(url string, code int) http.Handler {
  return &amp;redirectHandler{url, code}
}

func ErrorHandler(fn func(appengine.Context, http.ResponseWriter, *http.Request)) func(appengine.Context, http.ResponseWriter, *http.Request) {
  return func(c appengine.Context, w http.ResponseWriter, r *http.Request) {
    defer func() {
      if err, ok := recover().(error); ok {
        c.Errorf(&quot;%v&quot;, err)
        w.WriteHeader(http.StatusInternalServerError)
        errorTemplate.Execute(w, err)
      }
    }()
    AddSafeHeaders(w)
    fn(c, w, r)
  }
}

// Check aborts the current execution if err is non-nil.
func Check(err error) {
  if err != nil {
    panic(err)
  }
}

var errorTemplate = template.Must(template.New(&quot;error&quot;).Parse(errorTemplateHTML))

const errorTemplateHTML = `
&lt;html&gt;
&lt;head&gt;
        &lt;title&gt;XXX&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
        &lt;h2&gt;An error occurred:&lt;/h2&gt;
        &lt;p&gt;{{.}}&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
`

答案4

得分: 0

http.ResponseWriter 是一个接口。

你应该不要使用指向接口的指针。在net/http/server.go中,未导出的response结构体是实际实现ResponseWriter的类型,当服务器调用你的处理程序时,重要的是,当服务器实际调用处理程序的ServeHTTP时,它传递了一个***response**。它已经是一个指针,但你看不到,因为ResponseWriter是一个接口。(响应指针是在这里创建的这里,通过(c *conn).readRequest。(链接可能是错误的行数,但你应该能够找到它们)。

这就是为什么ServeHTTP函数需要实现Handler的原因:

ServeHTTP(w ResponseWriter, r *Request)

即不是指向ResponseWriter的指针,因为这个声明已经允许指向实现ResponseWriter接口的结构体的指针。

英文:

http.ResponseWriter is an interface.

You should probably not be using a pointer to an interface. In net/http/server.go, the unexported response struct is the actual type that implements ResponseWriter when the server calls your handler, and importantly, when the server actually calls the handler's ServeHTTP, it passes a *response. It's already a pointer, but you don't see that because ResonseWriter is an interface. (the response pointer is created here, by (c *conn).readRequest. (The links will likely be for the wrong lines the future, but you should be able to locate them).

That's why the ServeHTTP function required to implement Handler is:

ServeHTTP(w ResponseWriter, r *Request)

i.e. not a pointer to ResponseWriter, as this declaration already permits a pointer to a struct that implements the ResponseWriter interface.

答案5

得分: 0

由于我对Go语言还不熟悉,所以我创建了一个基于elithrar的答案的最小化的例子,展示了如何轻松地为所有路由/响应添加标头。我们通过创建一个满足http.HandlerFunc接口的函数,然后包装路由处理函数来实现这一点:

package main

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/gorilla/mux"
)

// Hello world.
func Hello(w http.ResponseWriter, r *http.Request) {
	json.NewEncoder(w).Encode("Hello World")
}

// HelloTwo world
func HelloTwo(w http.ResponseWriter, r *http.Request) {
	json.NewEncoder(w).Encode("Hello Two World")
}

// JSONHeaders满足http.HandlerFunc接口,并将Content-Type: application/json标头添加到每个响应中。
func JSONHeaders(handler http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		handler(w, r)
	}
}

func main() {
	router := mux.NewRouter()
	// 现在,不再直接调用处理函数,而是将其传递给包装函数。
	router.HandleFunc("/", JSONHeaders(Hello)).Methods("GET")
	router.HandleFunc("/hellotwo", JSONHeaders(HelloTwo)).Methods("GET")

	log.Fatal(http.ListenAndServe(":3000", router))
}

结果:

$ go run test.go &
$ curl -i localhost:3000/
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 28 Feb 2019 22:27:04 GMT
Content-Length: 14

"Hello World"
英文:

As I am new to Go, I created a minimal contrived example, based on elithrar's answer, which shows how to easily add headers to all your routes / responses. We do so, by creating a function that satisfies the http.HandlerFunc interface, then wraps the route handler functions:

package main

import (
	&quot;encoding/json&quot;
	&quot;log&quot;
	&quot;net/http&quot;


	&quot;github.com/gorilla/mux&quot;
)


// Hello world.
func Hello(w http.ResponseWriter, r *http.Request) {
	json.NewEncoder(w).Encode(&quot;Hello World&quot;)
}

// HelloTwo world
func HelloTwo(w http.ResponseWriter, r *http.Request) {
	json.NewEncoder(w).Encode(&quot;Hello Two World&quot;)
}

// JSONHeaders conforms to the http.HandlerFunc interface, and
// adds the Content-Type: application/json header to each response.
func JSONHeaders(handler http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)
		handler(w, r)
	}
}

func main() {	
	router := mux.NewRouter()
    // Now, instead of calling your handler function directly, pass it into the wrapper function.
	router.HandleFunc(&quot;/&quot;, JSONHeaders(Hello)).Methods(&quot;GET&quot;) 
	router.HandleFunc(&quot;/hellotwo&quot;, JSONHeaders(HelloTwo)).Methods(&quot;GET&quot;)

	log.Fatal(http.ListenAndServe(&quot;:3000&quot;, router))
}

Results:

$ go run test.go &amp;
$ curl -i localhost:3000/
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 28 Feb 2019 22:27:04 GMT
Content-Length: 14

&quot;Hello World&quot;

</details>



# 答案6
**得分**: 0

```go
// 接受用户提供的 http.HandlerFunc 并以各种方式进行修改。在这种情况下,它添加了两个新的头部。
func CommonlHandler(h http.HandlerFunc) http.HandlerFunc {
    return func (rs http.ResponseWriter, rq *http.Request) {
        rs.Header().Add("Server", "Some server")
        rs.Header().Add("Cache-Control", "no-store")
        h(rs, rq)
}

// 在设置 http 请求处理程序的某个地方

serveMux := http.NewServeMux()

serveMux.HandleFunc("/", CommonHandler(func(rs http.ResponseWriter, rq *http.Request) {
    // 像往常一样处理请求。由于它被包装在 CommonHandler 中,并且我们在那里设置了一些头部,对于对“/”的请求的响应将包含这些头部。
    // 在这里要注意你所做的修改。例如,如果你决定要应用不同的缓存策略而不是通用策略,由于这将传递给 CommonHandler,你的更改将被覆盖和丢失。因此,在决定创建头部之前,在 CommonHandler 中引入检查是否存在该头部可能是一个好主意。
}))

serveMux.HandleFunc("/contact", CommonHandler(func(rs http.ResponseWriter, rq *http.Request) {
    // 像往常一样处理另一个请求
}))
英文:

What i end up doing:

// Accepts a user supplied http.HandlerFunc and then modifies it in various ways. In this case, it adds two new headers.
func CommonlHandler(h http.HandlerFunc) http.HandlerFunc {
    return func (rs http.ResponseWriter, rq *http.Request) {
        rs.Header().Add(&quot;Server&quot;, &quot;Some server&quot;)
        rs.Header().Add(&quot;Cache-Control&quot;, &quot;no-store&quot;)
        h(rs, rq)
}

// somewhere down the line, where you&#39;re setting up http request handlers

serveMux := http.NewServeMux()

serveMux.HandleFunc(&quot;/&quot;, CommonHandler(func(rs http.ResponseWriter, rq *http.Request) {
    // Handle request as usual. Since it&#39;s wrapped in the CommonHandler and we&#39;ve set some headers there, responses to requests to &quot;/&quot; will contain those headers.
    // Be mindful what modifications you&#39;re doing here. If, for ex., you decide you want to apply different caching strategy than the Common one, since this will be passed to the CommonHandler, your changes will be overwritten and lost. So it may be a good idea to introduce checks in CommonHandler to determine whether the header exists, before you decide to create it.
}))

serveMux.HandleFunc(&quot;/contact&quot;, CommonHandler(func(rs http.ResponseWriter, rq *http.Request) {
    // handle another request as usual
}))

huangapple
  • 本文由 发表于 2013年7月14日 03:20:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/17633441.html
匿名

发表评论

匿名网友

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

确定