🛫

路由设置

配置路由之前必须初始一个路由实例,如:r := zweb.New()
支持的基础函数如下所示:
r.GET(path string, handle znet.Handler) r.POST(path string, handle znet.Handler) r.PUT(path string, handle znet.Handler) r.DELETE(path string, handle znet.Handler) r.PATCH(path string, handle znet.Handler) r.HEAD(path string, handle znet.Handler) r.OPTIONS(path string, handle znet.Handler) // 如需注册一个可以响应任何 HTTP 请求的路由可以使用 Any r.Any(path string, handle znet.Handler)
 

普通路由

r.GET("/hi", func(c *znet.Context) { c.String(200, "Hello world") }) r.POST("/ping", func(c *znet.Context) error { c.String(200, "Hello ping") // 如果返回错误会忽略上面的输出,转而输出 500 + err.Error() // 可以通过 znet.RewriteErrorHandler 中间件自定义 return nil }) // 还可以直接返回固定内容 r.GET("/ping", "pong")

正则路由

r.GET("/info/{id:[\d]+}", func(c *znet.Context) { id := c.GetParam("id") c.String(200, "Hello " + id) }) // 内置了几个别名简写, :id :full(等同*) // 如果非上面两个别名则等同 ([^\/.]+) // 上面写法可以简写为下面方式 r.GET("/info/:id", func(c *znet.Context) { // 注意::id 内置是只匹配数字的 id := c.GetParam("id") c.String(200, "Hello " + id) }) r.GET("/user/*", func(c *znet.Context) { // 注意:: 等同 .* // 其实也可以 /user/{*:.*} all:= c.GetParam("*") c.String(200, all) }) r.GET("/user/:name", func(c *znet.Context) { // 注意::name 等同 ([^\/.]+),不一定是要name 其他的英文字符也可以 name:= c.GetParam("name") c.String(200, "Hello " + name) }) // 多个匹配 r.GET(`/file/(?P<name>[\w\d-]+).(?P<ext>[a-zA-Z]+)`, func(c *znet.Context) { file := c.GetParam("name")+"."+c.GetParam("ext") c.String(200, file) })

结构体路由

// 默认情况是把方法的名称转蛇形命名并且用 - 分隔的 // 有需要可以修改 znet.BindStructDelimiter = "_" // 如果 BindStructDelimiter 为空则路由使用大驼峰而不是转蛇形命名 // 结构体 方法名:请求方式+路由(大驼峰) type testController struct {} // 路由组前缀 var prefix = "/test" // 该方法在绑定路由之前执行 func (t *testController) Init(r *znet.Engine) error { // 这里可以手动绑定一些特殊路由或者中间件之类 return nil } // Get /test/user func (t *testController) GETUser(c *znet.Context) {} // Post /test/user-info func (t *testController) POSTUserInfo(c *znet.Context) {} // 有三个特殊的前缀(ID,Full, Name)等同正则路由的 :id 与 :full(等同*) // Get /test/user/:id func (t *testController) IDGETUser(c *znet.Context) {} // Get /test/user/:full func (t *testController) FullGETUser(c *znet.Context) {} // 绑定路由 err := r.BindStruct(prefix, &testController{})

中间件

支持直接给路由组或者单个路由注册
PS. 所有支持 net 标准库的中间件包都可使用
// r.Use(middleware ...znet.HandlerFunc) // 给当前 r 注册一个中间件 r.Use(func(c *znet.Context) { // 执行下一个 Handler c.Next() // 获取需要输出的内容 // c.PrevContent() }) // 单独给 / 注册中间件,支持多个 r.GET("/", func(c *znet.Context) { c.String(200, "哈哈") },func(c *znet.Context) { c.Log.Debug("中间件前置处理") c.Next() c.Log.Debug("中间件后置处理") },func(c *znet.Context) { c.Log.Debug("第二个中间件前置处理") c.Next() c.Log.Debug("第二个中间件后置处理") })
 
优先级中间件 znet.WrapFirstMiddleware
默认情况下中间件是按传递的顺序执行的,假如想把某个中间件的执行时机提升至前面可以这样:
r.Use(func(c *znet.Context) { c.Log.Debug(1) c.Next() c.Log.Debug(2) }) r.GET("/", func(c *znet.Context) { c.String(200, "哈哈") },znet.WrapFirstMiddleware(func(c *znet.Context) { c.Log.Debug(3) c.Next() c.Log.Debug(4) })) // 执行之后的打印顺序就是: 3, 1, 4, 2
 

静态文件

// 把路由 /static/ 绑定到当前目录下的 static 目录 r.Static("/static/", "./static/") // 两种写法一样效果 r.StaticFS("/static/", http.Dir("./static/")) // 或者 r.Any("/static/*", func(c *znet.Context) { c.File("./static/" + c.GetParam("*")) }) // 指定文件 r.StaticFile("/m.jpg", "./static/my.jpg")
 

路由分组

r.Group("/debug", func(g *znet.Engine) { // g.Use() // 可以注册该分组使用的中间件 // 路由 /debug/group g.GET("group", func(c *znet.Context) { c.String(200, "Hello group") }) // 路由 /test/group g.GET("test/group", func(c *znet.Context) { c.String(200, "Hello group") }) })
 

未匹配路由

r.NotFoundHandler(func(c *znet.Context) { c.String(404, "NotFoundFunc") })
 

异常处理

r.Use(znet.Recovery(func(c *znet.Context, err error) { zlog.Error("Recovery", err) }))
 

全局预处理

r.PreHandler(func(c *znet.Context) (stop bool) { // 这里的处理是在中间件之前的 // code ... return })
 

依赖注入

znet.Handler 支持依赖注入,所以路由中间件处理都支持依赖注入,更多用法参考【依赖注入
注入 Gorm
r := znet.New() // 注入一个按需延迟调用变量,如果没有用到是不会初始化的 r.Injector.Provide(func() *gorm.DB { // 初始化并且返回 gorm db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) zerror.Panic(err) return db }) // Conf 配置结构体 type Conf struct { Addr string } // 直接注入一个初始化好的变量 r.Injector.Maps(Conf{Addr: "127.0.0.1"}) // 调用方式一 r.GET("/", func(c *Context){ var db *gorm.DB var conf Conf c.Injector.Resolve(&db, &conf) // 这里就可以调用上面注入的 db 和 conf }) // 调用方式二 r.GET("/", func(db *gorm.DB, conf Conf){ // 这里调用 db 和 conf })
 

其他

不支持 REST 怎么办
PS. 考虑到有些客户端可能不支持 REST(如PUT、DELETE等请求方法),可以使用 POST 方式来模拟
// 设置一下模拟请求类型的字段,设置一个特殊的字段 r.SetCustomMethodField("__TYPE__") // 只要 POST 请求的 header 内带有设置的字段就相当于该值的请求方式,下面就是把 POST 转换成 DELETE curl --location --request POST 'http://127.0.0.1:3788/' --header '__TYPE__: DELETE'