How can I pass data from middleware to handlers?

后端 未结 3 519
小鲜肉
小鲜肉 2021-01-04 01:27

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.         


        
相关标签:
3条回答
  • 2021-01-04 01:46

    Since you're already using Gorilla take a look at the context package.

    (This is nice if you don't want to change your method signatures.)

    import (
        "github.com/gorilla/context"
    )
    
    ...
    
    func Middleware(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // Middleware operations
            // Parse body/get token.
            context.Set(r, "token", token)
    
            next.ServeHTTP(w, r)
        })
    }
    
    ...
    
    func Handler() http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            token := context.Get(r, "token")
        })
    }
    
    0 讨论(0)
  • 2021-01-04 01:50

    The proper way to pass request scoped data would now be the context package in the standard library.

    https://golang.org/pkg/context/

    You can access it with request.Context on an http.Request.

    0 讨论(0)
  • 2021-01-04 02:03

    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.

    0 讨论(0)
提交回复
热议问题