design-patterns - 如何将golang请求中的上下文传递给中间件

标签 design-patterns go request timeout

我试图了解 Golang 1.7 中引入的上下文是如何工作的,以及将它传递给中间件和 HandlerFunc 的合适方法是什么。上下文是否应该在主函数中初始化并传递给 checkAuth 函数?以及如何将其传递给 HanlderServeHTTP 函数? 我读了Go concurrency patternsHow to use Context但我很难使这些模式适应我的代码。

func checkAuth(authToken string) util.Middleware {
    return func(h http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            if r.Header.Get("Auth") != authToken {
                util.SendError(w, "...", http.StatusForbidden, false)
                return
            }
            h.ServeHTTP(w, r)
        })
    }
}

// Handler is a struct
type Handler struct {
    ...
    ...
}

// ServeHTTP is the handler response to an HTTP request
func (h *HandlerW) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    decoder := json.NewDecoder(r.Body)

    // decode request / context and get params
    var p params
    err := decoder.Decode(&p)
    if err != nil {
       ...
        return
    }

    // perform GET request and pass context
    ...


}


func main() {
    router := mux.NewRouter()

    // How to pass context to authCheck?
    authToken, ok := getAuthToken()
    if !ok {
        panic("...")
    }
    authCheck := checkAuth(authToken)

    // initialize middleware handlers
    h := Handler{
       ...
   } 

   // chain middleware handlers and pass context
   router.Handle("/hello", util.UseMiddleware(authCheck, Handler, ...))
}

最佳答案

如果您查看 first example at that Go Concurrency Patterns blog post ,您会注意到它们是从 Background 上下文“派生”它们的上下文。结合 Request 对象上的 ContextWithContext 方法,可以满足您的需求。

我刚刚弄明白了(这不是我第一次阅读这些文档);当您“导出”一个上下文时,您正在通过一次更改创建另一个上下文。我已经包装了 http.Handler(实际上是使用 httprouter.Handle)。 Request.Context 的妙处在于它永远不会返回 nil;如果没有创建其他上下文,则返回后台上下文。

要指定超时,在您的处理程序中(就在您的“//执行 GET 请求”评论上方),您可以执行以下操作:

ctx, cancel := context.WithTimeout(r.Context(), time.Duration(60*time.Second))
defer cancel()
r = r.WithContext(ctx)

第一行创建上下文并为您提供取消 Hook ,您可以推迟;一旦请求得到服务,执行此延迟调用(第 2 行)时,任何派生上下文(也就是您添加变量的上下文)都将被取消。最后,第 3 行替换了请求,该请求现在包含更新后的上下文。

在您的授权检查器中,一旦确定用户有效,就可以在调用 ServeHTTP 之前将用户信息添加到上下文中。上下文的键不能使用内置类型,但您可以创建一个新类型,它只是内置类型的别名。为您的键值定义常量也是一个好主意。一个例子:

type ContextKey string

const ContextUserKey ContextKey = "user"

// Then, just above your call to ServeHTTP...

ctx := context.WithValue(r.Context(), ContextUserKey, "theuser")
h.ServeHTTP(w, r.WithContext(ctx))

这会将现在两次派生的上下文(现在具有超时用户 ID)传递给处理程序。

最后,拼图的最后一 block - 如何从处理程序中获取用户 ID。这是最直接的部分;我们只需对从请求的 Context 方法返回的值使用 Value 方法。类型是 interface{},因此如果您想将其视为字符串(如本例所示),则需要类型断言。

user := r.Context().Value(ContextUserKey)
doSomethingForThisUser(user.(string))

您不限于对每种方法进行一次更改;只要你继续派生相同的上下文,一旦请求被服务,它就会被清理干净,当最初派生的上下文(在这个例子中,我们指定超时的那个)在延迟 cancel() 调用触发。

关于design-patterns - 如何将golang请求中的上下文传递给中间件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39946583/

相关文章:

linux - 去编程linux amd64错误释放对象

go - 导入错误:版本号未知版本无效

file - 如何使用Golang在网络打印机上打印文件或格式文本

node.js - SailsJS req.body undefined

node.js - 如何修改管道以返回自定义响应?

php - 用于 Code Igniter 模型的完整抽象工厂模式

javascript - 您将如何组织大型复杂的 Web 应用程序(参见基本示例)?

iphone - iPhone 的设计模式 -> Web 服务功能?

json - 如果 Content-Type 是 application/json,空体是否正确?

c++ - 从多个表中读取数据的最佳方法