对于如何将数据从处理程序传递到包装的处理程序,我有很扎实的把握,但是有一种惯用的方式从包装的处理程序中取回东西吗?这是一个激励性的示例:我有一个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。我对这种方法不满意:
authHandler
现在对accessLog
包具有包级别的依赖关系,因为我将类型断言为*AccessLog
。 authHandler
可以采用某种方式将用户数据通知给请求堆栈的任何部分,而无需将自身紧密耦合到所述部分。 最佳答案
上下文本身是一个接口(interface),因此您可以在logger中间件中创建一个新的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))
}
}
关于go - 如何将数据传递给父中间件?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42401197/