dongliang1941 2017-02-22 20:04
浏览 11

如何将数据传递给父中间件?

I've got a pretty solid grip on how to pass data from a handler to the handler it wraps, but is there a go idiomatic way to get something back from the wrapped handler? Here's a motivating example: I have an accessLogHandler and an authHandler. accessLogHandler logs every http request, with timings and other request info such as the currently logged in user's ID (if there is one). authHandler is for routes that need a logged in user, it 403's when a user isn't logged in. I want to wrap some (but perhaps not all) of my routes with the authHandler, and wrap all of my routes with the accessLogHandler. If a user is logged in, I would like my accessLogHandler to log the user info along with the access log.

Now, I have a solution I've come up with that I don't like. I'll add the code and then explain some of my issues with it.

// Log the timings of each request optionally including user data
// if there is a logged in user
func accessLogHandler(fn http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        accessLog := newAccessLog()
        ctx := context.WithValue(r.Context(), accessLogKey, accessLog)
        fn.ServeHTTP(w, r.WithContext(ctx))

        // Logs the http access, ommit user info if not set
        accessLog.Log()
    }
}

// pull some junk off the request/cookies/whatever and check if somebody is logged in
func authHandler(fn http.HandlerFunc) http.HandlerFunc {
    return func (w http.ResponseWriter, r *http.Request) {
        //Do some authorization
        user, err := auth(r)
        if err != nil{
            //No userId, don't set anything on the accesslogger
            w.WriteHeader(http.StatusForbiddend)
            return
        }
        //Success a user is logged in, let's make sure the access logger knows
        acessLog := r.Context().Value(accessLogKey).(*AccessLog)
        accessLog.Set("userID", user.ID)
        fn.ServeHTTP(w, r)
    }
}

Basically, what I'm doing here is attaching an accessLog struct to my context inside the accessLogHandler and inside the authHandler I'm reading accessLog from the context and calling accessLog.Set to inform the logger that a userID is present.

Some things I don't like about this approach:

  1. context is immutable, but I'm sticking a mutable struct on it and mutating said struct elsewhere downstream. Feels like a hack.
  2. My authHandler now has a package level dependency on the accessLog package, since I'm type asserting to *AccessLog.
  3. Ideally my authHandler would have some way of informing any part of the request stack about user data without tightly coupling itself to said parts.
  • 写回答

1条回答 默认 最新

  • douzhicong5965 2017-02-22 22:54
    关注

    Context itself is an interface, so you could create a new logger context in the logger middleware that has the methods you would need to get the behavior you are after.

    Something like this:

    type Logger struct{}
    
    func (l *Logger) SetLogField(key string, value interface{}) {// set log field }
    func (l *Logger) Log(){// log request}
    
    type LoggerCtx struct {
        context.Context
        *Logger
    }
    
    func newAccessLog() *Logger {
        return &Logger{}
    }
    
    func accessLogHandler(fn http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            // create new logger context
            ctx := &LoggerCtx{}
            ctx.Context = r.Context()
            ctx.Logger = newAccessLog()
    
            fn.ServeHTTP(w, r.WithContext(ctx))
    
            // Logs the http access, ommit user info if not set
            ctx.Log()
        }
    }
    
    // pull some junk off the request/cookies/whatever and check if somebody is logged in
    func authHandler(fn http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            //Do some authorization
            user, err := auth(r)
            if err != nil {
                //No userId, don't set anything on the accesslogger
                w.WriteHeader(http.StatusForbiddend)
                return
            }
    
            //Success a user is logged in, let's make sure the access logger knows
            ctx := r.Context()
    
            // this could be moved - here for clarity
            type setLog interface {
                SetLogField(string, interface{})
            }
    
            if lctx, ok := ctx.(setLog); ok {
                lctx.SetLogField("userID", user.ID)
            }
    
            fn.ServeHTTP(w, r.WithContext(ctx))
        }
    }
    
    评论

报告相同问题?

悬赏问题

  • ¥15 神经网络预测均方误差很小 但是图像上看着差别太大
  • ¥15 Oracle中如何从clob类型截取特定字符串后面的字符
  • ¥15 想通过pywinauto自动电机应用程序按钮,但是找不到应用程序按钮信息
  • ¥15 如何在炒股软件中,爬到我想看的日k线
  • ¥15 seatunnel 怎么配置Elasticsearch
  • ¥15 PSCAD安装问题 ERROR: Visual Studio 2013, 2015, 2017 or 2019 is not found in the system.
  • ¥15 (标签-MATLAB|关键词-多址)
  • ¥15 关于#MATLAB#的问题,如何解决?(相关搜索:信噪比,系统容量)
  • ¥500 52810做蓝牙接受端
  • ¥15 基于PLC的三轴机械手程序