Gin自定义路由匹配
Gin路由系统可扩展自定义匹配逻辑,实现特殊的路由需求。
路由匹配扩展点
NoRoute处理器
Go
// 404处理
r.NoRoute(func(c *gin.Context) {
c.JSON(404, gin.H{
"code": 404,
"message": "路由不存在",
"path": c.Request.URL.Path,
})
})
// 多个NoRoute处理器形成链
r.NoRoute(NotFoundLogger(), NotFoundHandler())
NoMethod处理器
Go
// 405处理
r.NoMethod(func(c *gin.Context) {
c.JSON(405, gin.H{
"code": 405,
"message": "方法不允许",
"method": c.Request.Method,
"path": c.Request.URL.Path,
})
})
自定义路由条件
基于Header匹配
Go
func HeaderRouterMiddleware(routes map[string]gin.HandlerFunc) gin.HandlerFunc {
return func(c *gin.Context) {
version := c.GetHeader("X-API-Version")
if handler, ok := routes[version]; ok {
handler(c)
c.Abort()
return
}
c.Next()
}
}
// 使用:按版本头路由
r.Use(HeaderRouterMiddleware{
"v1": v1Handler,
"v2": v2Handler,
})
基于Host匹配
Go
func HostRouterMiddleware(hosts map[string]gin.HandlerFunc) gin.HandlerFunc {
return func(c *gin.Context) {
host := c.Request.Host
if handler, ok := hosts[host]; ok {
handler(c)
c.Abort()
return
}
c.Next()
}
}
// 使用:多域名路由
r.Use(HostRouterMiddleware{
"api.example.com": apiHandler,
"admin.example.com": adminHandler,
})
动态路由注册
运行时添加路由
Go
type DynamicRouter struct {
engine *gin.Engine
routes map[string]gin.HandlerFunc
}
func (dr *DynamicRouter) AddRoute(path string, handler gin.HandlerFunc) {
dr.routes[path] = handler
// Gin不支持运行时修改路由树,需要重启
}
// 使用中间件模拟动态路由
func DynamicRouteMiddleware(dr *DynamicRouter) gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Request.URL.Path
if handler, ok := dr.routes[path]; ok {
handler(c)
c.Abort()
return
}
c.Next()
}
}
路由分组策略
版本路由
Go
r := gin.New()
// v1版本
v1 := r.Group("/api/v1")
v1.Use(V1Middleware())
{
v1.GET("/users", v1GetUsers)
v1.POST("/users", v1CreateUser)
}
// v2版本
v2 := r.Group("/api/v2")
v2.Use(V2Middleware())
{
v2.GET("/users", v2GetUsers)
v2.POST("/users", v2CreateUser)
}
// 版本兼容路由
r.GET("/api/users", func(c *gin.Context) {
version := c.GetHeader("X-API-Version")
switch version {
case "v2":
v2GetUsers(c)
default:
v1GetUsers(c)
}
})
租户路由
Go
func TenantMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
tenantID := c.Param("tenant")
c.Set("tenantID", tenantID)
c.Next()
}
}
// 租户路由组
r.GET("/:tenant/users", TenantMiddleware(), getUsers)
r.POST("/:tenant/users", TenantMiddleware(), createUser)
// 处理器中获取租户ID
func getUsers(c *gin.Context) {
tenantID := c.GetString("tenantID")
users := getUsersByTenant(tenantID)
c.JSON(200, users)
}
路由重定向
自动重定向
Go
// 开启自动重定向(处理斜杠差异)
r.RedirectFixedPath = true
// /users 自动重定向到 /users/
// /users/ 自动重定向到 /users
手动重定向
Go
r.GET("/old-path", func(c *gin.Context) {
c.Redirect(301, "/new-path")
})
r.GET("/redirect/:id", func(c *gin.Context) {
id := c.Param("id")
c.Redirect(302, "/detail/"+id)
})
自定义匹配器
正则路由
Go
func RegexMiddleware(pattern string, handler gin.HandlerFunc) gin.HandlerFunc {
regex := regexp.MustCompile(pattern)
return func(c *gin.Context) {
path := c.Request.URL.Path
if regex.MatchString(path) {
// 提取匹配组作为参数
matches := regex.FindStringSubmatch(path)
for i, name := range regex.SubexpNames() {
if i > 0 && name != "" {
c.Set(name, matches[i])
}
}
handler(c)
c.Abort()
return
}
c.Next()
}
}
// 使用:正则匹配数字ID
r.Use(RegexMiddleware(`/users/(?P<id>\d+)/profile`, profileHandler))
条件组合匹配
Go
func CombinedMatcher(conditions ...func(*gin.Context) bool) gin.HandlerFunc {
return func(c *gin.Context) {
for _, cond := range conditions {
if !cond(c) {
c.Next()
return
}
}
// 所有条件满足,执行特殊处理
handleSpecialRequest(c)
c.Abort()
}
}
// 使用:组合条件
r.Use(CombinedMatcher(
func(c *gin.Context) bool { return c.Request.Method == "GET" },
func(c *gin.Context) bool { return strings.HasPrefix(c.Request.URL.Path, "/api/admin") },
func(c *gin.Context) bool { return c.GetHeader("X-Admin-Token") != "" },
))
路由优先级控制
| 路由类型 | 优先级 | 匹配规则 |
|---|---|---|
| 静态路由 | 最高 | 精确匹配 |
| 参数路由 | 中 | :param匹配 |
| 通配路由 | 最低 | *path匹配 |
注意:静态路由优先于参数路由,避免路由冲突。
要点总结
- NoRoute处理404,NoMethod处理405
- 中间件可实现Header、Host等条件路由
- 版本路由通过路由组或中间件实现
- 正则匹配通过自定义中间件扩展
- 遵循静态优先于参数的路由优先级规则
📝 发现内容有误?点击此处直接编辑