在go中为多个处理程序设置http标头

时间:2013-07-13 19:20:29

标签: 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)
    }
}

6 个答案:

答案 0 :(得分: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")

如果我没有弄错的话,那应该编译并且行为正常。

答案 1 :(得分:2)

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

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

如果要为每个请求设置“全局”标头,可以创建一个满足http.HandleFuncgo.auth has a good example)的函数,然后像这样包装处理程序:

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

另请查看net/http文档which has further examples

答案 2 :(得分: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")
}

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

// 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("error").Parse(errorTemplateHTML))

const errorTemplateHTML = `
<html>
<head>
        <title>XXX</title>
</head>
<body>
        <h2>An error occurred:</h2>
        <p>{{.}}</p>
</body>
</html>
`

答案 3 :(得分:0)

http.ResponseWriter是一个界面。

您可能不应该使用指向接口的指针。在net / http / server.go中,未导出的response结构是服务器调用处理程序时实现ResponseWriter的实际类型,重要的是when the server actually calls the handler's ServeHTTP,它传递了 *response 即可。它已经已经一个指针,但你没有看到,因为ResonseWriter是一个接口。 (响应指针由here创建(c *conn).readRequest。(链接可能是未来的错误行,但您应该能够找到它们。)

这就是实现ServeHTTP所需的Handler功能的原因:

ServeHTTP(w ResponseWriter, r *Request)

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

答案 4 :(得分: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 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("Content-Type", "application/json")
        handler(w, r)
    }
}

func main() {   
    router := mux.NewRouter()
    // Now, instead of calling your handler function directly, pass it into the wrapper function.
    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"

答案 5 :(得分:0)

我最终要做什么:

// 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("Server", "Some server")
        rs.Header().Add("Cache-Control", "no-store")
        h(rs, rq)
}

// somewhere down the line, where you're setting up http request handlers

serveMux := http.NewServeMux()

serveMux.HandleFunc("/", CommonHandler(func(rs http.ResponseWriter, rq *http.Request) {
    // Handle request as usual. Since it's wrapped in the CommonHandler and we've set some headers there, responses to requests to "/" will contain those headers.
    // Be mindful what modifications you'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("/contact", CommonHandler(func(rs http.ResponseWriter, rq *http.Request) {
    // handle another request as usual
}))