假设我正在构建一个Go Web应用程序,具有以下要求:
context.Context
乍一看,这似乎很容易:
r.Use(authMiddleware)
r.Use(loggingMiddleware)
// Other middlewares/routes
但是如果authMiddleware发出400,401,403或类似的错误,则会失败,因为这样就不会调用日志记录中间件。
所以重新订购似乎是合适的:
r.Use(loggingMiddleware)
r.Use(authMiddleware)
// Other middlewares/routes
但现在,假设我使用context.WithValue()
设置了身份验证信息,则记录器不知道身份验证信息。它仍然可以记录HTTP响应代码,大小等,但不记录经过身份验证的用户等。
这导致了一种非常复杂的解决方案:
r.Use(injectAuthPlaceholder)
r.Use(loggingMiddleware)
r.Use(authMiddleware)
injectAuthPlaceholder
的作用类似于:
var user *string
ctx = context.WithValue(ctx, userKey, user)
然后authMiddleware
为用户设置:
userPtr = ctx.Value(userKey).(*string)
*userPtr = authedUsername
这具有让记录器访问经过身份验证的用户名的效果,但它需要一个由两部分组成的身份验证机制,它似乎违反惯用法context.Context
,并且感觉很糟糕。
对于这种鸡蛋问题,有什么更惯用的解决方案?
答案 0 :(得分:0)
我记录的请求,包括身份验证信息,以及其他相关信息(请求的URL,响应代码,响应大小等)
根据我对你在这两个中间件中所使用的逻辑的理解,我感觉日志记录中间件混合了问题。我认为HTTP请求信息日志应该与身份验证日志分开。
正如我所说,日志记录是关于记录您的应用中与您相关的事件。此外,日志需要上下文相关,因此需要对他们记录的内容有一个深入的了解。
配置auth涉及提供用户数据库,加密方法,机密等。配置日志记录涉及提供日志记录格式和日志记录目标(例如文件或远程服务器)。这些之间有0重叠。它们的相关方式与HTTP请求与日志记录相关。 如果合并日志记录和auth中间件是有意义的,那么合并日志记录和请求处理是有意义的。还有其他一切。
是的,中间件有点像去包。就像标准的go包一样,我不会基于它们的抽象层来拆分它们,而是基于它们解决的域的部分。因此,如果我有一个包auth
,我会在其中添加所有身份验证逻辑,从域模型到传输层(例如http)。我强烈建议您Marcus Olsson
excellent talk关于DDD应用于Go。
中间件示例
func mwLogging(next MiddlewareFunc) MiddlewareFunc {
return func(c *Context) {
c.Ctx.Trace("h.http.req.start", "Request start",
log.String("method", c.Method),
log.String("path", c.Path),
log.String("user_agent", c.Req.Header.Get("User-Agent")),
)
next(c)
c.Ctx.Trace("h.http.req.end", "Request end",
log.Int("status", c.Res.Code()),
log.Duration("duration", time.Since(c.StartTime)),
)
}
}
func mwAuth(next MiddlewareFunc) MiddlewareFunc {
return func(c *http.Context) {
// Just an example
session, err := Auth(c.Req.Header.Get("Authorization"))
if err != nil {
c.Ctx.Warning("http.auth.error", "Authentication failed", log.Error(err))
c.Res.WriteHeader(http.StatusUnauthorized)
return
}
// You could store the session in context here
context.WithValue(ctx, "session", session)
next(c)
}
}
无耻插件:logging middleware取自stairlin/lego。