从Golang中间件发回响应的最佳方法是什么

时间:2018-02-21 06:16:47

标签: go middleware

我使用适配器模式创建了中间件。我的一个中间件是用于身份验证。因此,如果用户未经授权,则必须向用户发回响应,并且不应调用下一个中间件。

// 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循环否则即使在发送响应后它也会调用下一个中间件。

如果有其他方法可以处理这种情况,请告诉我。

3 个答案:

答案 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。