Go Echo 学习笔记(十二)中间件


中间件

中间件是在 HTTP 请求-响应处理周期过程中切入的一个函数(可以拦截http请求-响应生命周期的特殊函数),具有对 Echo#Context 的访问权,可以用于执行特定的操作,例如,记录每个请求或者限制请求的数量。

在请求-响应生命周期中可以注册多个中间件,每个中间件执行不同的功能,一个中间执行完再轮到下一个中间件执行。当所有的中间件执行完毕之后才会去执行请求处理函数(Hanlder)。

下图是中间件的运行流程:

Echo 中注册一个中间件所使用的方法是 Echo#Use() ,并且只有在完成注册之后的中间件才会在请求处理过程中被调用。

中间件具有的级别有:根级别(路由处理之前、理路由处理之后)、组级别、路径级别。

根级别(路由处理之前)

Echo#Pre() 支持注册一个中间件在请求被处理之前执行,这类中间件有助于对请求属性进行任意更改操作,但是由于路由处理程序尚未处理请求,因此该级别的中间件将无法从 echo.Context 访问任何与路径相关的API。

下面的这些内置的中间件应该被注册在这个级别:

  • HTTPSRedirect:重定向中间件,http强制跳转至https
  • HTTPSWWWRedirect:重定向中间件,https www跳转
  • WWWRedirect:重定向中间件,www跳转
  • NonWWWRedirect:重定向中间件,直接跳转
  • AddTrailingSlash
  • RemoveTrailingSlash
  • MethodOverride
  • Rewrite:url重定向中间,我们可以用于将一个url重定向到另外一个url。

根级别(路由处理之后)

大多数情况下使用 Echo#Use() 注册的中间件都是属于这个级别的,也就是在路由处理之后执行,这些中间件可以支持访问 echo.Context 相关的API。

下面的这些内置的中间件应该被注册在这个级别:

  • BodyLimit
  • Logger:Logger中间件主要用于打印http请求日志。
  • Gzip
  • Recover:Recover中间件,主要用于拦截panic错误并且在控制台打印错误日志,避免echo程序直接崩溃,一般echo应用都会注册Recover中间件,避免程序崩溃退出。
  • BasicAuth
  • JWTAuth
  • Secure
  • CORS
  • Static

组级别

可以针对一个 Echo 中的 Group 创建设置对应的中间件:

// 方法一
e := echo.New()
admin := e.Group("/admin", middleware.BasicAuth())

// 方法二
e := echo.New()
admin := e.Group("/admin")
admin.Use(middleware.BasicAuth())

路径级别

当注册一个路由之后也可以专门针对该路由设置对应的中间件:

e := echo.New()
e.GET("/", <Handler>, <Middleware...>)

跳过中间件

某些情况下需要跳过某些中间件,这种情况下可以在注册中间件的时候可以针对 middleware.LoggerConfig 设置其中的 Skipper func(c echo.Context) bool 方法来实现:

e := echo.New()
// 针对请求前缀为 localhost 的请求跳过Logger中间件
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
	Skipper: func(c echo.Context) bool {
		if strings.HasPrefix(c.Request().Host, "localhost") {
			return true
		}
		return false
	},
}))

自定义中间件

下面以一个简单的统计访问量的例子介绍如何自定义中间件:

package main

import (
	"net/http"
	"github.com/labstack/echo"
)

//记录访问量
var totalRequests  = 0

//中间件函数
func Count(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		//在这里处理拦截请求的逻辑
		//累计访问量
		totalRequests++
		
		//在响应头中输出访问量
		c.Response().Header().Add("requests", fmt.Sprintf("%d", totalRequests))

		//执行下一个中间件或者执行控制器函数, 然后返回执行结果
		return next(c)
	}
}

func main() {
	//初始化echo实例
	e := echo.New()
	//注册中间件
	e.Use(Count)
	
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, World!")
	})
	
	e.Logger.Fatal(e.Start(":1323"))
}