我已经非常可靠地掌握了如何将数据从处理程序传递到它包装的处理程序,但是有没有一种惯用的方式从包装的处理程序中获取某些东西?这是一个激励性的例子:我有accessLogHandler
和authHandler
。 accessLogHandler
记录每个http请求,包括时间和其他请求信息,例如当前登录用户的ID(如果有的话)。 authHandler
用于需要登录用户的路由,当用户没有登录时,它是403。我想用{{包裹一些(但可能不是全部)我的路由1}},用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
以通知记录器存在用户ID。
我不喜欢这种做法的一些事情:
accessLog.Set
现在对authHandler
包具有包级依赖性,因为我键入了断言为accessLog
。*AccessLog
可以通过某种方式通知请求堆栈的任何部分,而不会将自身与所述部分紧密耦合。答案 0 :(得分:1)
Context本身是一个接口,因此您可以在logger中间件中创建一个新的记录器上下文,该中间件具有获取您所追求的行为所需的方法。
这样的事情:
type Logger struct{}
func (l *Logger) SetLogField(key string, value interface{}) {// set log field }
func (l *Logger) Log(){// log request}
type LoggerCtx struct {
context.Context
*Logger
}
func newAccessLog() *Logger {
return &Logger{}
}
func accessLogHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// create new logger context
ctx := &LoggerCtx{}
ctx.Context = r.Context()
ctx.Logger = newAccessLog()
fn.ServeHTTP(w, r.WithContext(ctx))
// Logs the http access, ommit user info if not set
ctx.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
ctx := r.Context()
// this could be moved - here for clarity
type setLog interface {
SetLogField(string, interface{})
}
if lctx, ok := ctx.(setLog); ok {
lctx.SetLogField("userID", user.ID)
}
fn.ServeHTTP(w, r.WithContext(ctx))
}
}