How can I combine Go middleware pattern with error returning request handlers?

后端 未结 4 1942
青春惊慌失措
青春惊慌失措 2021-01-05 02:39

I am familiar with the Go middleware pattern like this:

// Pattern for writing HTTP middleware.
func middlewareHandler(next http.Handler) http.Handler {
             


        
4条回答
  •  天涯浪人
    2021-01-05 03:38

    The most flexible solution would be like this:

    First define a type that matches your handler signature and implement ServeHTTP to satisfy the http.Handler interface. By doing so, ServeHTTP will be able to call the handler function and process the error if it fails. Something like:

    type httpHandlerWithError func(http.ResponseWriter, *http.Request) error
    
    func (fn httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        if err := fn(w, r); err != nil {
             http.Error(w, err.Message, err.StatusCode)
        }
    }
    

    Now create the middleware as usual. The middleware should create a function which returns an error if it fails or calls the next in the chain on success. Then convert the function to the defined type something like:

    func AuthMiddleware(next http.Handler) http.Handler {
    
        // create handler which returns error
        fn := func(w http.ResponseWriter, r *http.Request) error {
    
            //a custom error value
            unauthorizedError := &httpError{Code: http.StatusUnauthorized, Message: http.StatusText(http.StatusUnauthorized)}
    
            auth := r.Header.Get("authorization")
            creds := credentialsFromHeader(auth)
    
            if creds != nil {
                return unauthorizedError
            }
    
            user, err := db.ReadUser(creds.username)
            if err != nil {
                return &httpError{Code: http.StatusInternalServerError, Message: http.StatusText(http.StatusInternalServerError)}
            }
    
            err = checkPassword(creds.password+user.Salt, user.Hash)
            if err != nil {
                return unauthorizedError
            }
    
            ctx := r.Context()
            userCtx := UserToCtx(ctx, user)
    
            // we got here so there was no error
            next.ServeHTTP(w, r.WithContext(userCtx))
            return nil
        }
    
        // convert function
        return httpHandlerWithError(fn)
    }
    

    Now you can use the middleware as you would use any regular middleware.

提交回复
热议问题