Golang中使用Http Context添加用户信息的方法

Golang中使用Http Context添加用户信息的方法 我正在尝试在一个中间件的 ServeHTTP http.Handler 函数中,设置一个包含用户信息的新上下文。我可以将新上下文传递给同一页面内的一个函数,并且它能正确打印出新上下文。然而,当我将其传递给下一个中间件处理器时,新的上下文值却是空的。在这个例子中,下一个中间件是一个记录日志的函数,它包装了整个请求,如下所示:

wrappedMux := NewLogger(NewEnsureAuth(NewResponseHeader(mux, “X-Launchpad-Request-Id”, uuidV2.String())))

以下是相关代码:

// 在同一文件内。
type EnsureAuth struct {
        handler http.Handler
}

func doSomething(ctx context.Context) {
        fmt.Printf("doSomething: myKey's value is %s\n", ctx.Value("Launchpad"))
        // 输出是:"doSomething: myKey's value is {Bugs Bunny bugs@large.net}"
}

func (ea *EnsureAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {

        if authResult.Authenticated {
                // 创建一个包含已认证用户信息的新请求上下文
                newCtx := context.WithValue(r.Context(), "Launchpad", authResult.User)

                // 使用新上下文创建一个新请求
                rWithUser := r.WithContext(newCtx)
                doSomething(rWithUser.Context())

                r.URL.Path = "/index.html"
                w.Header().Set("Content-Type", "text/html")
                w.WriteHeader(http.StatusOK)

                // 调用实际的处理器,传递新请求
                ea.handler.ServeHTTP(w, rWithUser)
        }
}

// 在另一个文件中。
func (l *Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(os.Stderr, "Your User: %v", r.Context().Value("Launchpad"))

        // 输出是:"Your User: <nil>"
}

非常感谢任何帮助。如果需要我提供更多细节,请告诉我。


更多关于Golang中使用Http Context添加用户信息的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

关于我在传递用户上下文时遇到的问题,我认为问题可能在于首次中间件调用日志处理器(用于获取请求开始时间)时,尚未拥有用户上下文。

下一个中间件对用户进行身份验证并添加用户上下文,之后我确实能够查看并打印用户上下文。然而,当其他中间件链执行完毕,我们返回到日志记录器进行实际日志记录时,用户上下文再次缺失。

我无法完全确定,但看起来日志记录中间件仍在使用原始的 HTTP 请求,尽管我能在前一个中间件的 ServeHTTP 调用请求中看到用户上下文。

如果有人能对此提供一些见解,我将不胜感激。

更多关于Golang中使用Http Context添加用户信息的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


问题在于你修改了请求的上下文,但没有将这个修改后的请求传递给后续的中间件。在你的 EnsureAuth.ServeHTTP 方法中,你创建了 rWithUser 但随后又修改了原始请求 r 的路径,这可能导致上下文没有正确传递。

以下是修正后的代码示例:

func (ea *EnsureAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if authResult.Authenticated {
        // 创建包含用户信息的新上下文
        newCtx := context.WithValue(r.Context(), "Launchpad", authResult.User)
        
        // 使用新上下文创建新请求
        rWithUser := r.WithContext(newCtx)
        
        // 修改新请求的路径
        rWithUser.URL.Path = "/index.html"
        
        // 设置响应头
        w.Header().Set("Content-Type", "text/html")
        
        // 测试:在同一中间件内访问上下文
        doSomething(rWithUser.Context())
        
        // 传递修改后的请求给下一个处理器
        ea.handler.ServeHTTP(w, rWithUser)
    } else {
        // 处理未认证的情况
        w.WriteHeader(http.StatusUnauthorized)
    }
}

关键点:

  1. 所有对请求的修改(包括路径修改)都应该在 rWithUser 上进行,而不是原始请求 r
  2. 确保将 rWithUser 传递给 ea.handler.ServeHTTP()
  3. 在日志中间件中,上下文值应该能正常访问:
func (l *Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 现在应该能正确获取到用户信息
    if user := r.Context().Value("Launchpad"); user != nil {
        fmt.Fprintf(os.Stderr, "Your User: %v\n", user)
    } else {
        fmt.Fprintf(os.Stderr, "No user found in context\n")
    }
    
    // 继续处理请求
    l.handler.ServeHTTP(w, r)
}

这样修改后,上下文值应该能在整个中间件链中正确传递。

回到顶部