http 中间件中的 Go 和指针

问题描述

我试图在我的网络服务器上记录一些数据,所以我创建了一个 loggingMiddleware 来为下一个请求提供服务,然后记录数据,我想这样我就会在 r *http.Request 中包含所有必要的数据{1}} 指针

return http.HandlerFunc(func(w http.ResponseWriter,r *http.Request) {
        // call next handler
        next.ServeHTTP(o,r)

        // get requestID
        reqID := middleware.GetReqID(r.Context())

        log.WithFields(
            log.Fields{
                "request_id":     reqID,// drops requestID
                "method":         r.Method,// http method
                "remote_address": r.RemoteAddr,// remote address
                "url":            r.URL.String(),// url used by the client to access the service
                "referer":        r.Referer(),// Referer header if present
                "user_agent":     r.UserAgent(),// User-Agent header
                "content_length": r.ContentLength,// Content-Length header if present"
            },).Info()
    })

然而,对于 RequestID,只有 RequestID 中间件安装在 loggingMiddleware 之前才是正确的

非工作

...
    // get a new chi rotuter
    router = chi.NewRouter()

    // MIDDLEWARES

    // log
    // use logrus to log requests
    router.Use(LoggingMiddleware)

    // requestID
    // Generate a unique id for every request
    // ids are grouped based on client hostname
    router.Use(middleware.RequestID)
...

工作

...
    // get a new chi rotuter
    router = chi.NewRouter()

    // MIDDLEWARES

    // requestID
    // Generate a unique id for every request
    // ids are grouped based on client hostname
    router.Use(middleware.RequestID)

    // log
    // use logrus to log requests
    router.Use(LoggingMiddleware)
...

这是预期的行为吗? r *http.Request 指针是否应该指向请求的“更新”版本?有没有办法解决这个问题? 因为,例如,如果我想从 JWT 令牌中提取用户名并将其放入 r.Context() 以便我可以稍后记录它,这将需要在 loggingMiddleware 之前安装一个单独的中间件。

对不起,我的英语不好,请询问是否有不清楚的地方。

谢谢

解决方法

middleware.RequestID 使用 http.Request.WithContext 将请求 ID 添加到请求上下文:

ctx = context.WithValue(ctx,RequestIDKey,requestID)
next.ServeHTTP(w,r.WithContext(ctx))

根据文档:

WithContext 返回 r 的浅拷贝,其上下文更改为 ctx。提供的 ctx 必须非 nil。

因此,因为它“返回 r 的浅拷贝”,并且它是(指向)这个浅拷贝传递给 next.ServeHTTP,如果 middleware.RequestID 被第二次挂载,那么 { r 中的 {1}} 指向的 LoggingMiddleware 与包含修改上下文的 *http.Request 不同,因此其上下文将不包含请求 ID。另一方面,如果 middleware.RequestID 首先被挂载,那么 LoggingMiddleware 将收到一个指向 r.WithContext 返回的浅拷贝的指针,并且一切都会按预期进行。