gqlgen 从解析器设置 cookie

gqlgen Set cookie from resolver

我正在使用 gin 和 gqlgen。 我需要从解析器设置 cookie,但解析器中只有来自 graphQL 的上下文和输入。 此问题已在 github 中得到解答。 但是这个不同,因为我无法更改 ctx.Writer.Write,当您尝试传入 ctx.Next 时什么也没有。因为 gin 不是那样工作的。

func (r *mutationResolver) Login(ctx context.Context, email string, password string) (bool, error) {
        // You need ctx.Writer to set a cookie and can't access that from here
}

我已经解决了这个问题,我想在下面回答我自己的问题。

简答



服务器端cookie说明:cookie是通过headers设置的,浏览器就是这样理解你要设置cookie的,需要ctx.Writer写headers。最后,“您想在解析器中访问 ctx.Writer”。


你的中间件中有一个ctx(在我的例子中它是杜松子酒,它是ctx.Request.Context()),你有一个ctx.Writer,这是你需要写的到您的 headers 并设置 cookie。

你应该把你的 ctx.Writer 放到你的 ctx.Request.Context() 中,因为你的 ctx.Request 正转到你的解析器,而不是 hole ctx!
然后你就可以访问你的作家了。

完整答案


middleware 中你必须构建一个结构 object,将 ctx.Writer 传递给它并设置一个指向 ctx.Request.Context 的指针并设置一个方法来为你设置 cookie。

type CookieAccess struct {
    Writer     http.ResponseWriter
    UserId     uint64
    IsLoggedIn bool
}
// method to write cookie
func (this *CookieAccess) SetToken(token string) {
    http.SetCookie(this.Writer, &http.Cookie{
        Name:     cookieName,
        Value:    token,
        HttpOnly: true,
        Path:     "/",
        Expires:  time.Now().Add(token_expire),
    })
}

在你的 middleware:

func extractUserId(ctx *gin.Context) (uint64, error) {
    c, err := ctx.Request.Cookie(cookieName)
    if err != nil {
        return 0, errors.New("There is no token in cookies")
    }

    userId, err := ParseToken(c.Value)
    if err != nil {
        return 0, err
    }
    return userId, nil
}

func setValInCtx(ctx *gin.Context, val interface{}) {
    newCtx := context.WithValue(ctx.Request.Context(), cookieAccessKeyCtx, val)
    ctx.Request = ctx.Request.WithContext(newCtx)
}

func Middleware() gin.HandlerFunc {
    return func(ctx *gin.Context) {
        cookieA := CookieAccess{
            Writer: ctx.Writer,
        }

        // &cookieA is a pointer so any changes in future is changing cookieA is context
        setValInCtx(ctx, &cookieA)

        userId, err := extractUserId(ctx)
        if err != nil {
            cookieA.IsLoggedIn = false
            ctx.Next()
            return
        }

        cookieA.UserId = userId
        cookieA.IsLoggedIn = true

       // calling the actual resolver
        ctx.Next()
       // here will execute after resolver and all other middlewares was called
       // so &cookieA is safe from garbage collector
    }
}

您必须在解析器中调用此函数。 它得到 ctx 并返回 &cookieA

func GetCookieAccess(ctx context.Context) *CookieAccess {
    return ctx.Value(cookieAccessKeyCtx).(*CookieAccess)
}

最后在您的 Login 解析器中:

CA := security.GetCookieAccess(ctx)
CA.SetToken(token)
CA.UserId = userId

我希望这会对某人有所帮助:)))