4

我正在尝试为多个处理程序设置一个 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)
    }
}
4

6 回答 6

4

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

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

由于运算符优先级,被解释为:

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

由于wr有 type *http.ResponseWriter,它是指向接口的指针,而不是接口本身,所以这不起作用。我假设您知道这一点,这就是您这样做的原因*wr。我假设您对编译器的暗示是:

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

如果我没记错的话,那应该可以编译并正常运行。

于 2013-07-13T22:21:51.167 回答
3

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

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

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

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

另请查看net/http文档,其中包含更多示例

于 2013-07-14T05:47:08.980 回答
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>
`
于 2013-07-16T00:31:19.553 回答
0

http.ResponseWriter是一个接口。

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

这就是为什么ServeHTTP需要实现的功能Handler是:

ServeHTTP(w ResponseWriter, r *Request)

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

于 2016-10-08T01:03:06.903 回答
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"
于 2019-02-28T22:29:25.830 回答
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
}))
于 2020-07-16T20:23:25.450 回答