This article was first published on the public account, concerned about the end of the public account reply to gohttp03 to obtain the article used complete source code.

Middleware is (usually) a small piece of code that accepts a request, processes it, each middleware does one thing, and when it’s done passes it on to another middleware or final handler, thus decoupling the program. Without middleware we would have to do this in the final handler, which would result in bloated handlers and low code reuse. Some common use cases for middleware are request logging, Header manipulation, HTTP request authentication, ResponseWriter hijacking, and so on.

Voiceover: The above description of middleware is almost identical to the one I wrote in Laravel source code parsing middleware two years ago. Once again, after doing development for a long time, it is sometimes more important to master some programming ideas than to master a programming language, which leads us to write middleware with Go again.

Creating middleware

Next we use Go to create middleware that takes http.handlerfunc as its argument only, wraps it in the middleware and returns the new HTTP.Handlerfunc for the server service multiplexer to call. Here we create a new type of Middleware that will make it easier to chain calls to multiple Middleware at the end.

type Middleware func(http.HandlerFunc) http.HandlerFunc
Copy the code

The following generic middleware code template makes writing middleware easier on a regular basis.

Middleware code template

Middleware is implemented using the decorator pattern. The following generic middleware code template makes it easier to write middleware. When we write middleware ourselves, we only need to fill the template with the code logic we need.

func createNewMiddleware(a) Middleware {
    Create a new middleware
    middleware := func(next http.HandlerFunc) http.HandlerFunc {
        // Create a new handler package for next
        handler := func(w http.ResponseWriter, r *http.Request) {

            // Middleware processing logic.// Call the next middleware or the final handler
            next(w, r)
        }

        // Returns the newly created wrapper handler
        return handler
    }

    // Returns the newly created middleware
    return middleware
}
Copy the code

Using middleware

We create two middleware, one to record the execution time of the program, and the other to verify whether the request is using the specified HTTP Method. After creation, we use the defined Chain function to link http.handlerfunc to the middleware applied to it. The middleware will execute in order of addition. Finally, the handler function is executed. The complete code is as follows:

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

type Middleware func(http.HandlerFunc) http.HandlerFunc// Record eachURLThe execution duration of the requestfunc Logging(a) Middleware {

    // Create middleware
    return func(f http.HandlerFunc) http.HandlerFunc {

        // Create a new handler wrapper for http.handlerfunc
        return func(w http.ResponseWriter, r *http.Request) {

            // Middleware processing logic
            start := time.Now()
            defer func(a) { log.Println(r.URL.Path, time.Since(start)) }()

            // Call the next middleware or the final handler
            f(w, r)
        }
    }
}

// Verify that the Request is using the specified HTTP Method, or return 400 Bad Request
func Method(m string) Middleware {

    return func(f http.HandlerFunc) http.HandlerFunc {

        return func(w http.ResponseWriter, r *http.Request) {

            ifr.Method ! = m { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)return
            }

            f(w, r)
        }
    }
}

// Middleware that applies to HTTP.HandlerFunc processors
// Chained to the processor itself in order for http.handlefunc calls
func Chain(f http.HandlerFunc, middlewares ... Middleware) http.HandlerFunc {
    for _, m := range middlewares {
        f = m(f)
    }
    return f
}

// The final http.handlerfunc to handle the request
func Hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello world")}func main(a) {
    http.HandleFunc("/", Chain(Hello, Method("GET"), Logging()))
    http.ListenAndServe(": 8080".nil)}Copy the code

After running the program, you will open the browser to visit http://localhost:8080 and get the following output:

2020/02/07 21:07:52/359.503 µs 2020/02/07 21:09:17/34.727 µsCopy the code

This is the end of how to write and use middleware with Go, in about 10 minutes. However, this is more about exploring the implementation principles, so how to use the middleware in a production environment, let’s move on.

usegorilla/muxApplication middleware

We discussed how to create middleware above, but using the Chain function to link multiple middleware and processors at a time is still a bit inconvenient, and we started using gorilla/ MUx’s Router as a Router in the last article. Fortunately, Gorrila.mux supports adding middleware to routers, and if a match is found, middleware is executed in the order in which middleware is added, including its child routers.

The Gorlila.mux router uses the Use method to add middleware to the router. The Use method is defined as follows:

func (r *Router) Use(mwf ... MiddlewareFunc) {for _, fn := range mwf {
		r.middlewares = append(r.middlewares, fn)
	}
}
Copy the code

MiddlewareFunc can accept multiple arguments of type mux.MiddlewareFunc, which is declared as:

type MiddlewareFunc func(http.Handler) http.Handler
Copy the code

Similar to the Middleware type defined above, the Middleware type is a function type, except that its parameters and return values are HTTP.Handler interfaces. HTTP.Handler is an interface defined in NET/HTTP to represent an object that handles an HTTP request. The object must implement the ServeHTTP method. We can create middleware that meets the requirements of Gorrila.mux by making minor changes to the above middleware template:

Func CreateMuxMiddleware() mux.middlewarefunc {// Create middlewarereturnHttp.handler {// Create a new Handler wrapper for http.handlerfuncreturnHttp.handlerfunc (func(w http.responsewriter, r * http.request) {// Middleware processing logic...... // Call the next middleware or final handler. F.sever HTTP(w, r)})}}Copy the code

Next, we’ll adapt the two middleware customizations above and apply them to the http_demo project we’ve been using, in log.go and http_method.go for ease of managing new middleware directories in the project

// Middleware /log.go func Logging() mux.middlewarefunc {// Create middlewarereturnHttp.handler {// Create a new Handler wrapper for http.handlerfuncreturnHttp.handlerfunc (func(w http.responsewriter, r * http.request) {start := time.now () deferfunc() {log.println (r.ul.path, time.since (start))}() // call the next middleware or the final handler. r) }) } } // middleware/http_demo.go func Method(m string) mux.MiddlewareFunc {return func(f http.Handler) http.Handler {

		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

			ifr.Method ! = m { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)return
			}

			f.ServeHTTP(w, r)
		})
	}
}
Copy the code

Then reference it in our router:

Func RegisterRoutes(r * mux.router) {r.us (middleware.logging ())"/index").Subrouter()
	indexRouter.Handle("/", &handler.HelloHandler{})

	userRouter := r.PathPrefix("/user").Subrouter()
	userRouter.HandleFunc("/names/{name}/countries/{country}", handler.ShowVisitorInfo)
	userRouter.Use(middleware.Method("GET")// Apply} to the child routerCopy the code

Recompile and start running the program after access

http://localhost:8080/user/names/James/countries/NewZealand
Copy the code

As you can see from the console, the processing time of this request is recorded:

2020/02/08 09:29:50 Starting HTTP server... 2020/02/08 09:55:20 / user/names/James/countries/NewZealan (including 51.157 sCopy the code

Here we finished exploring the process and principle of writing Web middleware, in the actual development only need to write middleware according to their own needs in accordance with the middleware code template we give, in writing middleware should also pay attention to their scope of responsibility, do not put all the logic in.

Previous review:

Learn more about writing HTTP servers with Go

Use Gorilla/MUx to enhance the Go HTTP server routing capabilities

You can get the complete source code of this article by replying to gohttp03 in the public account.