dseomy1964 2015-07-19 18:22
浏览 29

如何将数据从中间件传递给处理程序?

I am designing my handlers to return a http.Handler. Here's the design of my handlers:

 func Handler() http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  })
}

My middleware is designed to accept an http.Handler and then call the handler once the middleware has finished performing its operations. Here's the design of my middleware:

 func Middleware(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // Middleware operations

    next.ServeHTTP(w, r)
  })
}

Considering the design of my middleware and handlers, what is the proper way of passing information from the middleware to the handler? The information that I am trying to pass from my middleware to the handlers is a JSON web token parsed from the request body. If I do not pass the parsed JWT to the handler, then I will need to parse the JWT again in my handlers. Parsing the request body for a JWT in both the middleware and handler seems wasteful. Just in case this information is relevant, I am using the standard net/http library with gorilla mux.

  • 写回答

3条回答 默认 最新

  • duanqi5333 2015-07-19 18:28
    关注

    A first approach, similar to the question, is in codemodus/chain by Daved.

    Package chain aids the composition of Handler wrapper chains that carry request-scoped data.

    It uses the notion of Context, coupled with a Context handler:

    func ctxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
        // ...
    
        if s, ok := getMyString(ctx); ok {
            // s = "Send this down the line."
        }
    
        // ...
    }
    

    Another approach: You can have a look at "Custom Handlers and Avoiding Globals in Go Web Applications", by Matt Silverlock (elithrar). (full example here)

    The idea is to define ServeHTTP on a type which include the relevant context.

    // We've turned our original appHandler into a struct with two fields:
    // - A function type similar to our original handler type (but that now takes an *appContext)
    // - An embedded field of type *appContext
    type appHandler struct {
        *appContext
        h func(*appContext, http.ResponseWriter, *http.Request) (int, error)
    }
    
    // Our ServeHTTP method is mostly the same, and also has the ability to
    // access our *appContext's fields (templates, loggers, etc.) as well.
    func (ah appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        // Updated to pass ah.appContext as a parameter to our handler type.
        status, err := ah.h(ah.appContext, w, r)
        if err != nil {
            log.Printf("HTTP %d: %q", status, err)
            switch status {
            case http.StatusNotFound:
                http.NotFound(w, r)
                // And if we wanted a friendlier error page, we can
                // now leverage our context instance - e.g.
                // err := ah.renderTemplate(w, "http_404.tmpl", nil)
            case http.StatusInternalServerError:
                http.Error(w, http.StatusText(status), status)
            default:
                http.Error(w, http.StatusText(status), status)
            }
        }
    }
    

    In the appContext struct, you would put any data you want to pass around.

    评论

报告相同问题?