路由注册
Hertz 提供了 GET
、POST
、PUT
、DELETE
、ANY
等方法用于注册路由。
方法 | 介绍 |
---|---|
Hertz.GET |
用于注册 HTTP Method 为 GET 的方法 |
Hertz.POST |
用于注册 HTTP Method 为 POST 的方法 |
Hertz.DELETE |
用于注册 HTTP Method 为 DELETE 的方法 |
Hertz.PUT |
用于注册 HTTP Method 为 PUT 的方法 |
Hertz.PATCH |
用于注册 HTTP Method 为 PATCH 的方法 |
Hertz.HEAD |
用于注册 HTTP Method 为 HEAD 的方法 |
Hertz.OPTIONS |
用于注册 HTTP Method 为 OPTIONS 的方法 |
Hertz.Handle |
这个方法支持用户手动传入 HTTP Method 用来注册方法,当用于注册普通的 HTTP Method 方法时和上述的方法作用是一致的,并且这个方法同时也支持用于注册自定义 HTTP Method 方法 |
Hertz.Any |
用于注册所有 HTTP Method 方法 |
Hertz.StaticFile/Static/StaticFS |
用于注册静态文件 |
package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/hertz/pkg/protocol/consts"
)func main() {h := server.Default(server.WithHostPorts("127.0.0.1:8080"))// 当访问/时,允许以列表的形式显示文件夹的文件,这个文件夹就是 ./ 目录h.StaticFS("/", &app.FS{Root: "./", GenerateIndexPages: true})h.GET("/get", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "get")},)h.POST("/post", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "post")},)h.PUT("/put", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "put")},)h.DELETE("/delete", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "delete")},)h.PATCH("/patch", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "patch")},)h.HEAD("/head", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "head")},)h.OPTIONS("/options", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "options")},)h.Any("/ping_any", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "any")},)h.Handle("LOAD", "/load", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "load")},)h.Spin()
}
当访问 127.0.0.1:8080/ 时
路由组
Hertz 提供了路由组 ( Group
) 的能力,用于支持路由分组的功能,同时中间件也可以注册到路由组上。
package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/hertz/pkg/protocol/consts"
)func main() {h := server.Default(server.WithHostPorts("127.0.0.1:8080"))v1 := h.Group("/v1")v1.GET("/get", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "get")},)v1.POST("/post", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "post")},)v2 := h.Group("/v2")v2.PUT("/put", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "put")},)v2.DELETE("/delete", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "delete")},)h.Spin()
}
产生的路由
2025/02/23 15:02:29.967989 engine.go:669: [Debug] HERTZ: Method=GET absolutePath=/v1/get --> handlerName=main.main.func1 (num=2 handlers)
2025/02/23 15:02:29.974073 engine.go:669: [Debug] HERTZ: Method=POST absolutePath=/v1/post --> handlerName=main.main.func2 (num=2 handlers)
2025/02/23 15:02:29.974073 engine.go:669: [Debug] HERTZ: Method=PUT absolutePath=/v2/put --> handlerName=main.main.func3 (num=2 handlers)
2025/02/23 15:02:29.974073 engine.go:669: [Debug] HERTZ: Method=DELETE absolutePath=/v2/delete --> handlerName=main.main.func4 (num=2 handlers)
在路由组中使用中间件
如下示例在路由组中使用中间件。
示例代码
package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/hertz/pkg/common/hlog""github.com/cloudwego/hertz/pkg/protocol/consts"
)// 自定义中间件
func exampleMiddleware() app.HandlerFunc {return func(ctx context.Context, c *app.RequestContext) {// 在 Next 中的函数执行之前打印日志hlog.Info("print before...")// 使用 Next 使得路由匹配的函数执行c.Next(ctx)// 在 Next 中的函数执行之后打印日志hlog.Info("print after...")}
}func main() {h := server.Default(server.WithHostPorts("127.0.0.1:8080"))// use 中间件v1 := h.Group("/v1", exampleMiddleware())v1.GET("/ping", func(ctx context.Context, c *app.RequestContext) {hlog.Info("print in handler...")c.String(consts.StatusOK, "ping")},)h.Spin()
}
相当于
package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/hertz/pkg/common/hlog""github.com/cloudwego/hertz/pkg/protocol/consts"
)func exampleMiddleware() app.HandlerFunc {return func(ctx context.Context, c *app.RequestContext) {// 在 Next 中的函数执行之前打印日志hlog.Info("print before...")// 使用 Next 使得路由匹配的函数执行c.Next(ctx)// 在 Next 中的函数执行之后打印日志hlog.Info("print after...")}
}func main() {h := server.Default(server.WithHostPorts("127.0.0.1:8080"))v1 := h.Group("/v1")// use 中间件v1.Use(exampleMiddleware())v1.GET("/ping", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK,"ping")})h.Spin()
}
路由类型
Hertz 支持丰富的路由类型用于实现复杂的功能,包括静态路由、参数路由 (命名参数、通配参数)。
路由的优先级:静态路由
> 命名参数路由
> 通配参数路由
静态路由
就是非常普通的路由
h.GET("/get", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "get")},)
这里的 "/get"
就是静态路由
命名参数路由
Hertz 支持使用 :name
这样的命名参数设置路由,并且命名参数只匹配单个路径段。
如果我们设置 /user/:name
路由,匹配情况如下
路径 | 是否匹配 |
---|---|
/user/gordon | 匹配 |
/user/you | 匹配 |
/user/gordon/profile | 不匹配 |
/user/ | 不匹配 |
通过使用 RequestContext.Param
方法,我们可以获取路由中携带的参数。
示例代码:
package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/hertz/pkg/protocol/consts"
)func main() {h := server.Default(server.WithHostPorts("127.0.0.1:8080"))// 此处理程序将匹配: "/hertz/version", 但不会匹配 : "/hertz/" or "/hertz" or "/hertz/version/xxx"h.GET("/hertz/:version", func(ctx context.Context, c *app.RequestContext) {version := c.Param("version")c.String(consts.StatusOK, "Hello %s", version)},)h.Spin()
}
通配参数路由
Hertz 支持使用 *path
这样的通配参数设置路由,并且通配参数会匹配所有内容。
如果我们设置 /src/*path
路由,匹配情况如下
路径 | 是否匹配 |
---|---|
/src/ | 匹配 |
/src/somefile.go | 匹配 |
/src/subdir/somefile.go | 匹配 |
通过使用 RequestContext.Param
方法,我们可以获取路由中携带的参数。
示例代码:
package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/hertz/pkg/protocol/consts"
)func main() {h := server.Default(server.WithHostPorts("127.0.0.1:8080"))// 然而,这一个将匹配 "/hertz/v1/" 和 "/hertz/v2/send"h.GET("/hertz/:version/*action", func(ctx context.Context, c *app.RequestContext) {version := c.Param("version")action := c.Param("action")message := version + " is " + actionc.String(consts.StatusOK, message)},)h.Spin()
}
使用匿名函数与装饰器注册路由
在使用匿名函数或装饰器注册路由时,如果我们使用 RequestContext.HandlerName()
获取 handler 名称则会获取到错误的名称。
这里需要使用 Hertz 提供的 GETEX
、POSTEX
、PUTEX
、DELETEEX
、HEADEX
、AnyEX
、HandleEX
方法并手动传入 handler 名称注册路由,使用 app.GetHandlerName
获取 handler 名称。
示例代码:
package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/hertz/pkg/protocol/consts"
)func main() {h := server.Default()h.AnyEX("/ping", func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, app.GetHandlerName(c.Handler()))}, "ping_handler",)h.Spin()
}
获取路由注册信息
Hertz 提供了 Routes
获取注册的路由信息供用户使用。
func (engine *Engine) Routes() (routes RoutesInfo)
Routes
函数返回一个按 HTTP 方法划分的包含路由信息(HTTP 方法名,路由路径,请求处理函数名)的切片。
路由信息结构:
// RouteInfo represents a request route's specification which contains method and path and its handler.
type RouteInfo struct {Method string // http methodPath string // url pathHandler string // handler nameHandlerFunc app.HandlerFunc
}// RoutesInfo defines a RouteInfo array.
type RoutesInfo []RouteInfo
示例代码:
package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/hertz/pkg/common/hlog""github.com/cloudwego/hertz/pkg/common/utils""github.com/cloudwego/hertz/pkg/protocol/consts"
)func main() {h := server.Default()h.GET("/ping", func(ctx context.Context, c *app.RequestContext) {c.JSON(consts.StatusOK, utils.H{"ping": "pong"})},)routeInfo := h.Routes()for _, route := range routesInfo {hlog.Info(route.Handler, route.Path, route.Method)}h.Spin()
}
NoRoute 与 NoMethod 使用
Hertz 提供了 NoRoute
与 NoMethod
方法用于全局处理 HTTP 404 与 405 请求。
当使用 NoMethod
时需要与 WithHandleMethodNotAllowed
配合使用。
示例代码:
package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/hertz/pkg/common/utils""github.com/cloudwego/hertz/pkg/protocol/consts"
)func main() {h := server.Default(server.WithHandleMethodNotAllowed(true))h.POST("/ping", func(ctx context.Context, c *app.RequestContext) {c.JSON(consts.StatusOK, utils.H{"ping": "pong"})})// set NoRoute handlerh.NoRoute(func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "no route")})// set NoMethod handlerh.NoMethod(func(ctx context.Context, c *app.RequestContext) {c.String(consts.StatusOK, "no method")})h.Spin()
}
经测试,只有NoRoute有效,NoMethod无效
重定向尾斜杠
Hertz 在默认情况下会根据请求 path 末尾的 /
自动进行转发。
如果 router 中只有 /foo/,那么请求 /foo 会被自动重定向到 /foo/;
如果 router 中只有 /foo,那么 /foo/ 会被重定向到 /foo。
这样的请求除 GET
以外的请求方法都会触发 307 Temporary Redirect
状态码,而 GET
请求会触发 301 Moved Permanently
状态码。
可以在配置中取消,如下:
package mainimport "github.com/cloudwego/hertz/pkg/app/server"func main() {h := server.New(server.WithRedirectTrailingSlash(false))...
}