路由
Buffalo 使用 github.com/gorilla/mux 作为app的路由实现,同时也再封装了一层API。下面就是介绍路由相关的使用方法。
创建一个Buffalo应用(和路由)
应用的配置在app.go文件中:
a := buffalo.New(buffalo.Options{
Env: ENV,
SessionName: "_coke_session",
})
默认的配置满足大部分的需求,如果你需要自定义更多的选项,可以参照 godoc.org/github.com/gobuffalo/buf... 。
映射handler
所有的路由都应该对应一个buffalo.Handler的函数,这样的handler函数类似这样的:
func (c buffalo.Context) error {
// do some work
}
在MVC的模式中对应的其实就是Controller部分,主要app的逻辑实现部分。这些handler函数默认带了buffalo.Context结构,这个结构体包含了当前请求的所有信息。
看 上下文 去了解更多的关于该结构体的知识。
Buffalo支持如下http方法:
- GET
- POST
- PUT
- PATCH
- DELETE
- OPTIONS
- HEAD
也可以使用ANY来匹配所有的HTTP方法。
映射HTTP方法到handler的方法如下:
a.GET("/some/path", SomeHandler)
a.POST("/some/path", func (c buffalo.Context) error {
// do some work
})
// etc...
用内联的handler函数是一个推荐的方法。同时为了提高代码的可读性,我们也可以将一组相关的handler函数写到一个文件中,比如和用户相关的handler可以放在actions目录下一个user.go的文件中。需要自建,可以通过命令行创建,后面又介绍。
命名路由
默认情况下Buffalo是用路由的路径加上Path来命名的。比如 a.GET("/coke", CokeHandler) 的路由名称就是 cokePath。
a.GET("/coke", CokeHandler) // cokePath()
在视图中就可以使用路由名称来直接连接和跳转了。
<a href="<%= cokePath() %>">Coke</a>
我们可以通过命令行buffalo routes 来查看当前app下所有的路由及名称。
$ buffalo routes
METHOD | PATH | ALIASES | NAME | HANDLER
------ | ---- | ------- | ---- | -------
GET | / | | rootPath | github.com/markbates/coke/actions.HomeHandler
GET | /widgets/ | | widgetsPath | github.com/markbates/coke/actions.WidgetsResource.List
POST | /widgets/ | | widgetsPath | github.com/markbates/coke/actions.WidgetsResource.Create
GET | /widgets/new/ | | newWidgetsPath | github.com/markbates/coke/actions.WidgetsResource.New
GET | /widgets/{widget_id}/ | | widgetPath | github.com/markbates/coke/actions.WidgetsResource.Show
PUT | /widgets/{widget_id}/ | | widgetPath | github.com/markbates/coke/actions.WidgetsResource.Update
DELETE | /widgets/{widget_id}/ | | widgetPath | github.com/markbates/coke/actions.WidgetsResource.Destroy
GET | /widgets/{widget_id}/edit/ | | editWidgetPath | github.com/markbates/coke/actions.WidgetsResource.Edit
重要提示:由于默认的路由名称是根据路径来命名的,所以即使是对应的handler没有变只是路径变了,路由的名称也会跟着变。
app.Resource("/fooz", WidgetsResource{})
$ buffalo routes
METHOD | PATH | ALIASES | NAME | HANDLER
------ | ---- | ------- | ---- | -------
GET | / | | rootPath | github.com/markbates/coke/actions.HomeHandler
GET | /fooz/ | | foozPath | github.com/markbates/coke/actions.WidgetsResource.List
POST | /fooz/ | | foozPath | github.com/markbates/coke/actions.WidgetsResource.Create
GET | /fooz/new/ | | newFoozPath | github.com/markbates/coke/actions.WidgetsResource.New
GET | /fooz/{widget_id}/ | | foozPath | github.com/markbates/coke/actions.WidgetsResource.Show
PUT | /fooz/{widget_id}/ | | foozPath | github.com/markbates/coke/actions.WidgetsResource.Update
DELETE | /fooz/{widget_id}/ | | foozPath | github.com/markbates/coke/actions.WidgetsResource.Destroy
GET | /fooz/{widget_id}/edit/ | | editFoozPath | github.com/markbates/coke/actions.WidgetsResource.Edit
看 自定义路由名称 来查看如何改变默认生成的路由名称。
在视图模板中使用路由的助手方法
路由的助手方法可以直接在模板中使用:
<%= widgetsPath() %> // /widgets
也可以给路由助手类传参:
<%= editWidgetPath({widget_id: 1}) %> // /widgets/1/edit
在action中使用路由助手
直接跳转
可以通过路由的名称直接跳转到相应的地址
func MyHandler(c buffalo.Context) error {
return c.Redirect(307, "widgetsPath()")
// Or with parameters
return c.Redirect(307, "widgetPath()", render.Data{"widget_id": "1"})
}
查找和调用 路由助手
直接上代码:
func MyHandler(c buffalo.Context) error {
ri, err := App().Routes().Lookup("widgetPath")
if err != nil {
return errors.WithStack(err)
}
h := ri.BuildPathHelper()
u, err := h(render.Data{"widget_id": 1})
if err != nil {
return errors.WithStack(err)
}
return c.Redirect(307, string(u))
}
自定义路由
我们可以通过buffalo.RouteInfo#Name 方法来自定义路由的名称。
a.GET("/coke", CokeHandler).Name("customPath")
在视图模板中就可以直接调用了。
<a href="<%= customPath() %>">Coke</a>
参数
请求的参数可以用 buffalo.Context 中获取到:
a.GET("/users", func (c buffalo.Context) error {
return c.Render(200, r.String(c.Param("name")))
})
对于以上给出的例子,如果请求是 GET /users?name=ringo,那么将返回 200: ringo.
命名的参数
在路由的路径中指定名称的参数,也是通过 buffalo.Context#Param 方法获取的。
a.GET("/users/{name}", func (c buffalo.Context) error {
return c.Render(200, r.String(c.Param("name")))
})
对于以上的例子,如果请求是 GET /users/ringo, 那么也将返回 200: ringo.
a.GET("/users/new", func (c buffalo.Context) error {
return c.Render(200, r.String("new"))
})
a.GET("/users/{name}", func (c buffalo.Context) error {
return c.Render(200, r.String(c.Param("name")))
})
这两个路由非常相似,但是也没有问题。系统会优先匹配安全相等的路由。
正则表达式
github.com/gorilla/mux也支持正则表达式来匹配路由。
a.GET("/articles/{id:[0-9]+}", func (c buffalo.Context) error {
return c.Render(200, r.String(c.Param("id")))
})
路由组
在一些场景下,我们会用到路由组,比如一组路由使用一个中间件,或者api接口不用的版本。
g1 := a.Group("/api/v1")
g1.Use(APIAuthorizer)
g1.GET("/users", func (c buffalo.Context) error {
// responds to GET /api/v1/users
})
g2 := a.Group("/api/v2")
g2.Use(APIAuthorizer)
g2.GET("/users", func (c buffalo.Context) error {
// responds to GET /api/v2/users
})
默认的路由组也会继承自父的app。
a.Use(SomeMiddleware)
g := a.Group("/api/v1")
g.Use(APIAuthorizer)
在这种情况下 /api/v1 实际上是有两个中间件的,一个是自己的APIAuthorizer,另一个是继承来的SomeMiddleware。更多的中间配置,见中间件 章节。