路由分组与中间件:Group、Middleware 注册与执行顺序

引言

真实接口总有一批"公共逻辑":登录校验、访问日志、跨域处理。把这些逻辑抽成中间件,再配合路由分组挂载,代码就整洁了。本章讲 s.Groupgroup.Middleware

概念

  • 分组:s.Group(prefix, func(group *ghttp.RouterGroup){...}) 生成带前缀的路由组,组内可用 GET/POST/PUT/DELETE 等方法注册。
  • 中间件:形如 func(r *ghttp.Request) 的函数,内部调用 r.Middleware.Next() 放行到后续逻辑;Next() 之后的代码在业务处理返回后执行,形成"洋葱模型"。
  • 执行顺序:中间件按注册顺序依次进入,先注册的先执行前置部分;业务逻辑执行完后再按相反顺序执行后置部分(具体以官方文档中间件章节为准)。
  • 终止处理:中间件里不想继续时调用 r.Exit() 结束本次请求处理。

代码示例

先看一个登录校验中间件(很经典的写法):

// 登录校验:从参数取 token,为空则直接返回并终止
func MiddlewareAuth(r *ghttp.Request) {
	token := r.Get("token")
	if token.IsEmpty() {
		r.Response.WriteJson(g.Map{
			"code":    401,
			"message": "未登录或登录已过期",
		})
		r.Exit()
		return
	}
	r.Middleware.Next()
}

把它挂到分组上:

package main

import (
	"time"

	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

// 访问日志中间件:记录耗时,后置部分在业务返回后执行
func MiddlewareAccessLog(r *ghttp.Request) {
	begin := time.Now()
	r.Middleware.Next()
	g.Log().Infof(r.Context(), "耗时: %v ms", time.Since(begin).Milliseconds())
}

func main() {
	s := g.Server()

	s.Group("/api", func(group *ghttp.RouterGroup) {
		// 组内路由先经过中间件,按注册顺序执行
		group.Middleware(MiddlewareAuth, MiddlewareAccessLog)

		group.GET("/user", func(r *ghttp.Request) {
			r.Response.WriteJson(g.Map{"code": 0, "message": "ok"})
		})
		group.POST("/order", func(r *ghttp.Request) {
			r.Response.WriteJson(g.Map{"code": 0, "message": "created"})
		})
	})

	s.SetPort(8000)
	s.Run()
}

访问 GET /api/user?token=abc 会先过登录校验再过访问日志;不带 token 时在 MiddlewareAuth 中直接结束,业务函数不会执行。

注意点

  • 中间件里记得调用 r.Middleware.Next(),否则后续中间件与业务逻辑都不会执行。
  • r.Exit() 会终止本次请求的处理流程(含后续中间件),适合"校验失败提前返回"。
  • 全局中间件可用 s.Use(middleware...) 注册,作用于该服务全部路由;分组中间件只作用于组内。
  • 中间件的"洋葱"顺序在涉及响应改写时很关键,多中间件场景建议画出执行顺序再编码。

小结

分组解决"路由怎么组织",中间件解决"公共逻辑怎么复用",二者配合是工程化路由的核心。下一章进入请求参数:如何在处理函数里把用户提交的数据取出来。中间件特性细节以官方文档 goframe.org 为准。

笔记加载中…