为了账号安全,请及时绑定邮箱和手机立即绑定

在处理程序之后访问 HTTP 请求上下文

在处理程序之后访问 HTTP 请求上下文

Go
临摹微笑 2023-02-21 16:45:02
在我的日志记录中间件(链中的第一个)中,我需要访问一些上下文,这些上下文是在链下的某些 auth 中间件中编写的,并且仅在执行处理程序本身之后。旁注:需要首先调用日志记录中间件,因为我需要记录请求的持续时间,包括在中间件中花费的时间。此外,当权限不足时,auth 中间件能够中止请求。在那种情况下,我还需要记录失败的请求。我的问题是从指针读取上下文http.Request不会返回我期望它具有的身份验证数据。请参阅下面的示例:package mainimport (    "context"    "fmt"    "net/http"    "time")const (    contextKeyUsername = "username")func authMiddleware(next http.Handler) http.Handler {    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {        ctx := r.Context()        ctx = context.WithValue(ctx, contextKeyUsername, "user123")        next.ServeHTTP(w, r.WithContext(ctx))    })}func logMiddleware(next http.Handler) http.Handler {    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {        defer func(start time.Time) {            ctx := r.Context()            username := ctx.Value(contextKeyUsername)            if username != nil {                fmt.Printf("user %s has accessed %s, took %d\n", username,                    r.URL.Path, time.Since(start).Milliseconds())            } else {                fmt.Printf("annonyous has accessed %s, took %d\n",                    r.URL.Path, time.Since(start).Milliseconds())            }        }(time.Now())        next.ServeHTTP(w, r)    })}func welcome(w http.ResponseWriter, r *http.Request) {    ctx := r.Context()    username := ctx.Value(contextKeyUsername)    if username != nil {        fmt.Fprintf(w, fmt.Sprintf("hello %s", username.(string)))    } else {        fmt.Fprintf(w, "hello")    }}func main() {    mux := http.NewServeMux()    mux.HandleFunc("/welcome", welcome)    chain := logMiddleware(authMiddleware(mux))    http.ListenAndServe(":5050", chain)}虽然 get 请求127.0.0.1:5050/welcome确实返回了预期的字符串hello user123,但日志的输出是:annonyous has accessed /welcome, took 0由于请求是作为指针传递的,我预计在执行延迟时,上下文将包含预期username值。我在这里错过了什么?
查看完整描述

1 回答

?
慕尼黑8549860

TA贡献1818条经验 获得超11个赞

WithContext返回请求的浅表副本,即创建的请求与从中读取上下文的authMiddleware请求不同。logMiddleware

您可以让根中间件(在本例中为logMiddleware)创建带值的上下文和浅请求副本,但不是普通字符串在上下文中存储非零指针authMiddleware,然后使用指针间接分配指针指向的值,然后logMiddleware,在next退出后,可以取消引用该指针以访问该值。

并且为了避免不愉快的取消引用,您可以使用指向带有字符串字段的结构的指针,而不是指向字符串的指针。

type ctxKey uint8


const userKey ctxKey = 0


type user struct{ name string }


func logMiddleware(next http.Handler) http.Handler {

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

        u := new(user)

        r = r.WithContext(context.WithValue(r.Context(), userKey, u))


        defer func(start time.Time) {

            if u.name != "" {

                fmt.Printf("user %s has accessed %s, took %s\n", u.name, r.URL.Path, time.Since(start))

            } else {

                fmt.Printf("annonyous has accessed %s, took %s\n", r.URL.Path, time.Since(start))

            }

        }(time.Now())


        next.ServeHTTP(w, r)

    })

}

func authMiddleware(next http.Handler) http.Handler {

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

        if u, ok := r.Context().Value(userKey).(*user); ok {

            u.name = "user123"

        }

        next.ServeHTTP(w, r)

    })

}

func welcome(w http.ResponseWriter, r *http.Request) {

    if u, ok := r.Context().Value(userKey).(*user); ok && u.name != "" {

        fmt.Fprintf(w, "hello %s", u.name)

    } else {

        fmt.Fprintf(w, "hello")

    }

}

https://go.dev/play/p/N7vmjQ7iLM1


查看完整回答
反对 回复 2023-02-21
  • 1 回答
  • 0 关注
  • 107 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号