我使用适配器模式创建了中间件。我的一个中间件是用于身份验证。因此,如果用户未经授权,则必须向用户发回响应,并且不应调用下一个中间件。
// Adapter type
type Adapter func(http.Handler) http.Handler
// Adapt func
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
// Call all middleware
for _, adapter := range adapters {
h = adapter(h)
}
return h
}
// CheckAuth middleware
func CheckAuth() Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Get Authorization token from the header
// Validate the token
// if valid token then call h.ServeHTTP(w, r)
// else send response 401 to the user,
if(validUser){
h.ServeHTTP(w, r)
}else{
fmt.Fprintf(w, "Unauthorized")
}
return h
}
}
}
http.Handle("/", Adapt(indexHandler, AddHeader(),
CheckAuth(),
CopyMgoSession(db),
Notify(logger),
)
CheckAuth中间件中的只有在用户被授权时才调用h.ServeHTTP(w, r)
,因此对于其他情况我们还需要打破for
函数的Adapt
循环否则即使在发送响应后它也会调用下一个中间件。
如果有其他方法可以处理这种情况,请告诉我。
答案 0 :(得分:2)
链中的下一个中间件仅在显式调用它时才会运行。
下一个中间件将作为h
传递给您的闭包,并且您通过调用h.ServeHTTP()
来调用它。如果你不打电话,没有其他中间件运行,所以你必须提供完整的HTTP响应。
答案 1 :(得分:1)
中间件通常是链接的。有框架可以为您做到这一点。一个光滑的例子是Alice。
chain := alice.New(th.Throttle, timeoutHandler, nosurf.NewPure).Then(myHandler)
如果您想自己动手,可以使用递归来避免for循环。例如(来自此link):
// buildChain builds the middlware chain recursively, functions are first class
func buildChain(f http.HandlerFunc, m ...middleware) http.HandlerFunc {
// if our chain is done, use the original handlerfunc
if len(m) == 0 {
return f
}
// otherwise nest the handlerfuncs
return m[0](buildChain(f, m[1:cap(m)]...))
}
每个中间件都接收下一个参数。因此,下一个必须由前一个处理程序手动调用,否则链将停止。因此,在auth中间件中,如果auth失败并且链条停止并且您的错误状态是最后返回的内容,则不必调用下一个中间件。因此,在您的代码中,您需要接受http.Handler
的参数,这是下一个处理程序(中间件函数必须具有func(http.Handler) http.Handler
的签名)。有关详细信息,请参阅此blog。
您可能还想设置正确的http状态代码。包括这样的内容:
http.Error(w, "Forbidden: xyz", http.StatusForbidden)
答案 2 :(得分:1)
Adapt功能与服务请求无关。它在HTTP服务器启动之前执行一次(并且只执行一次)。请注意,它返回一个http.Handler,但它不是一个http.Handler本身。
在这种情况下,Adapt返回的处理程序的行为如下:
var indexHandler http.Handler
func handlerWithMiddleWare(w http.ResponseWriter, r *http.Request) {
notify := func(w http.ResponseWriter, r *http.Request) {
copyMgoSession := func(w http.ResponseWriter, r *http.Request) {
checkAuth := func(w http.ResponseWriter, r *http.Request) {
addHeader := func(w http.ResponseWriter, r *http.Request) {
indexHandler.ServeHTTP(w, r)
}
addHeader(w, r)
}
checkAuth(w, r)
}
copyMgoSession(w, r)
}
notify(w, r)
}
因此,如果您在不调用下一个中间件的情况下让CheckAuth返回,您可以发送您喜欢的任何响应;就像你在任何其他处理程序中一样。
顺便说一下,你希望让Adapt以相反的顺序迭代。我不确定你是否知道Notify首先执行,然后是CopyMgoSession,然后是CheckAuth,然后是AddHeader。