【发布时间】:2017-02-23 14:10:17
【问题描述】:
我非常清楚如何将数据从处理程序传递到它包装的处理程序,但是有没有一种惯用的方式从包装的处理程序中取回一些东西?这是一个鼓舞人心的例子:我有一个accessLogHandler 和一个authHandler。 accessLogHandler 记录每个 http 请求,包括时间和其他请求信息,例如当前登录的用户 ID(如果有)。 authHandler 用于需要登录用户的路由,当用户未登录时为 403。我想用 authHandler 包装我的一些(但可能不是全部)路由,并包装我的所有路由与accessLogHandler。如果用户已登录,我希望我的accessLogHandler 将用户信息与访问日志一起记录。
现在,我想出了一个我不喜欢的解决方案。我将添加代码,然后解释我的一些问题。
// Log the timings of each request optionally including user data
// if there is a logged in user
func accessLogHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
accessLog := newAccessLog()
ctx := context.WithValue(r.Context(), accessLogKey, accessLog)
fn.ServeHTTP(w, r.WithContext(ctx))
// Logs the http access, ommit user info if not set
accessLog.Log()
}
}
// pull some junk off the request/cookies/whatever and check if somebody is logged in
func authHandler(fn http.HandlerFunc) http.HandlerFunc {
return func (w http.ResponseWriter, r *http.Request) {
//Do some authorization
user, err := auth(r)
if err != nil{
//No userId, don't set anything on the accesslogger
w.WriteHeader(http.StatusForbiddend)
return
}
//Success a user is logged in, let's make sure the access logger knows
acessLog := r.Context().Value(accessLogKey).(*AccessLog)
accessLog.Set("userID", user.ID)
fn.ServeHTTP(w, r)
}
}
基本上,我在这里所做的是将accessLog 结构附加到accessLogHandler 和authHandler 内部的上下文中,我正在从上下文中读取accessLog 并调用accessLog.Set 来通知记录用户 ID 存在。
我不喜欢这种方法的一些地方:
- context 是不可变的,但我在其上粘贴了一个可变结构并在下游其他地方改变所述结构。感觉像个黑客。
- 我的
authHandler现在对accessLog包具有包级别依赖关系,因为我对*AccessLog进行了类型断言。 - 理想情况下,我的
authHandler可以通过某种方式通知请求堆栈的任何部分有关用户数据的信息,而无需将自身与所述部分紧密耦合。
【问题讨论】:
标签: go