前言
中间件通常位于前端的客户端站点请求和请求的后端资源之间。中间件非常有用,尤其是当我们希望在进行 API 调用之前进行一些验证,例如请求方法、标头和 JWT 等。
网络异常,图片无法展示
|
日志中间件
http.Handler
包装器是一个具有一个输入参数和一个输出参数的函数,两者都是 http.Handler
类型。
func Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { //code before make the backend api call next.ServeHTTP(w, r) //serve and handle the http request //code after make the backend api call }) }
现在,而不是直接服务和监听我们的 http 处理程序,例如:
http.ListenAndServe(":3000", handler)
我们现在可以将它包装在我们的中间件中并变成:
http.ListenAndServe(":3000", Middleware(handler))
接下来将介绍如何创建一个 Go 语言的日志中间件。
中间件只需将 http.HandlerFunc
作为其参数之一,将其包装并返回一个新的 http.HandlerFunc
供服务器调用。
新建一个 main.go
文件:
package main import ( "fmt" "log" "net/http" ) func logging(f http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { log.Println(r.URL.Path) f(w, r) } } func foo(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "foo") } func bar(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "bar") } func main() { http.HandleFunc("/foo", logging(foo)) http.HandleFunc("/bar", logging(bar)) http.ListenAndServe(":8080", nil) }
创建 mod 文件:
$ go mod init main.go go: creating new go.mod: module main.go go: to add module requirements and sums: go mod tidy
打开另一个终端:
$ curl -s http://localhost:8080/foo foo $ curl -s http://localhost:8080/bar bar $ curl -s http://localhost:8080/foo\?bar foo
运行上述代码:
$ go run main.go 2022/05/25 23:12:00 /foo 2022/05/25 23:12:09 /bar 2022/05/25 23:12:16 /foo
中间件框架
中间件本身只是将 a 作为其参数之一,将其包装并返回一个新的供服务器调用。在这里,我们定义了一种新类型,它最终使将多个中间件链接在一起变得更加容易。这个想法的灵感来自于 Mat Ryers 关于构建 API 的演讲。
这个片段详细解释了如何创建一个新的中间件。在下面的完整示例中,代码结构示例如下:
func createNewMiddleware() Middleware { // Create a new Middleware middleware := func(next http.HandlerFunc) http.HandlerFunc { // Define the http.HandlerFunc which is called by the server eventually handler := func(w http.ResponseWriter, r *http.Request) { // ... do middleware things // Call the next middleware/handler in chain next(w, r) } // Return newly created handler return handler } // Return newly created middleware return middleware }
完整测试
package main import ( "fmt" "log" "net/http" "time" ) type Middleware func(http.HandlerFunc) http.HandlerFunc // Logging logs all requests with its path and the time it took to process func Logging() Middleware { // Create a new Middleware return func(f http.HandlerFunc) http.HandlerFunc { // Define the http.HandlerFunc return func(w http.ResponseWriter, r *http.Request) { // Do middleware things start := time.Now() defer func() { log.Println(r.URL.Path, time.Since(start)) }() // Call the next middleware/handler in chain f(w, r) } } } // Method ensures that url can only be requested with a specific method, else returns a 400 Bad Request func Method(m string) Middleware { // Create a new Middleware return func(f http.HandlerFunc) http.HandlerFunc { // Define the http.HandlerFunc return func(w http.ResponseWriter, r *http.Request) { // Do middleware things if r.Method != m { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } // Call the next middleware/handler in chain f(w, r) } } } // Chain applies middlewares to a http.HandlerFunc func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc { for _, m := range middlewares { f = m(f) } return f } func Hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "hello world") } func main() { http.HandleFunc("/", Chain(Hello, Method("GET"), Logging())) http.ListenAndServe(":8080", nil) }
运行代码:
$ go run main.go
打开另外的终端:
$ curl -s http://localhost:8080/ hello world $ curl -s -XPOST http://localhost:8080/ Bad Request
$ go run main.go 2022/05/25 23:20:01 / 21.595µs 2022/05/25 23:20:13 / 5.129µs