name | sort |
---|---|
Routing |
2 |
This chapter will cover the three types of routers incorporated into Beego.
Beego supports a RESTful function router. This basic router includes the URI and closure functions.
web.Get("/",func(ctx *context.Context){
ctx.Output.Body([]byte("hello world"))
})
web.Post("/alice",func(ctx *context.Context){
ctx.Output.Body([]byte("bob"))
})
web.Any("/foo",func(ctx *context.Context){
ctx.Output.Body([]byte("bar"))
})
- web.Get(router, web.FilterFunc)
- web.Post(router, web.FilterFunc)
- web.Put(router, web.FilterFunc)
- web.Head(router, web.FilterFunc)
- web.Options(router, web.FilterFunc)
- web.Delete(router, web.FilterFunc)
- web.Any(router, web.FilterFunc)
In cases where packages such as net/http
are already implemented in a system they can be integrated into the web API or web system by following this procedure:
s := rpc.NewServer()
s.RegisterCodec(json.NewCodec(), "application/json")
s.RegisterService(new(HelloService), "")
web.Handler("/rpc", s)
beego.Handler(router, http.Handler)
the first parameter represents the URI, and the second parameter represents http.Handler
. When this is registered all requests to /rpc
will call http.Handler
.
There is also a third parameter, isPrefix
. If this parameter is set to true
all the matches will comply with prefix matching, meaning that the url /rpc/user
will also call the register. By default this value is false
.
RESTful is a popular approach to API development that Beego supports implicitly, executing Get
method for GET request and Post
method for POST request. The default router is RESTful.
A fixed router is a full matching router, such as:
web.Router("/", &controllers.MainController{})
web.Router("/admin", &admin.UserController{})
web.Router("/admin/index", &admin.ArticleController{})
web.Router("/admin/addpkg", &admin.AddController{})
The fixed routers above are typical RESTful routers in their most common configuration, with one fixed router and one controller. This results in the execution of a different method based on each request method.
To simplify router configuration, Beego uses the router implementation approach found in Sinatra to support many router types.
-
web.Router("/api/?:id", &controllers.RController{})
default matching /api/123 :id = 123 can match /api/
-
web.Router("/api/:id", &controllers.RController{})
default matching /api/123 :id = 123 can't match /api/
-
web.Router("/api/:id([0-9]+)", &controllers.RController{})
Customized regex matching /api/123 :id = 123
-
web.Router("/user/:username([\w]+)", &controllers.RController{})
Regex string matching /user/astaxie :username = astaxie
-
web.Router("/download/*.*", &controllers.RController{})
matching /download/file/api.xml :path= file/api :ext=xml
-
web.Router("/download/ceshi/*", &controllers.RController{})
full matching /download/ceshi/file/api.json :splat=file/api.json
-
web.Router("/:id:int", &controllers.RController{})
int type matching :id is int type. web implements ([0-9]+) for you
-
web.Router("/:hello:string", &controllers.RController{})
string type matching :hello is string type. web implements ([\w]+) for you
-
beego.Router("/cms_:id([0-9]+).html", &controllers.CmsController{})
has prefix regex :id is the regex. matching cms_123.html :id = 123
The variables can be accessed in the controller like this:
this.Ctx.Input.Param(":id")
this.Ctx.Input.Param(":username")
this.Ctx.Input.Param(":splat")
this.Ctx.Input.Param(":path")
this.Ctx.Input.Param(":ext")
The examples above use default method names, where the request method name is same as the controller method name. For example as GET
request executes Get
method and POST
request executes Post
method. Different controller method names can be set like this:
web.Router("/",&IndexController{},"*:Index")
Use the third parameter which is the method you want to call in the controller. Here are some rules:
-
- means any request method will execute this method.
- Use httpmethod:funcname format.
- Multiple formats can use
;
as the separator. - Many HTTP methods mapping the same funcname, use
,
as the separator for HTTP methods.
Below are some examples of RESTful design:
web.Router("/api/list",&RestController{},"*:ListFood")
web.Router("/api/create",&RestController{},"post:CreateFood")
web.Router("/api/update",&RestController{},"put:UpdateFood")
web.Router("/api/delete",&RestController{},"delete:DeleteFood")
Below is an example of multiple HTTP methods mapping to the same controller method:
web.Router("/api",&RestController{},"get,post:ApiFunc")
Below is an example of different HTTP methods mapping to different controller methods. ;
as the separator:
web.Router("/simple",&SimpleController{},"get:GetFunc;post:PostFunc")
Below are the acceptable HTTP methods:
- *:including all methods below
- get :GET request
- post :POST request
- put :PUT request
- delete :DELETE request
- patch :PATCH request
- options :OPTIONS request
- head :HEAD request
If * and other HTTP methods are used together the HTTP method will be executed first. For example:
web.Router("/simple",&SimpleController{},"*:AllFunc;post:PostFunc")
The PostFunc
rather than the AllFunc
will be executed for POST requests.
The router of custom methods does not support RESTful behaviour by default which means if you set the router like web.Router("/api",&RestController{},"post:ApiFunc")
and the request method is POST
then the Post
method won't be executed by default.
To use auto matching the controller must be registered as an auto-router.
web.AutoRouter(&controllers.ObjectController{})
Beego will retrieve all the methods in that controller by reflection. The related methods can be called like this:
/object/login will call Login method of ObjectController
/object/logout will call Logout method of ObjectController
Except /:controller/:method
will match to controller and method. The remainder of the url path will be parsed as GET parameters and saved into this.Ctx.Input.Param
:
/object/blog/2013/09/12 will call Blog method of ObjectController with parameters `map[0:2013 1:09 2:12]`.
URL will match by lowercase conversion, so object/LOGIN
will also map to Login
method.
All the urls below will map to the simple
method of controller
.
/controller/simple
/controller/simple.html
/controller/simple.json
/controller/simple.xml
The extension name of the url can be reached by accessing this.Ctx.Input.Param(":ext")
.
Not all routers need to be registered inside router.go
. Only the controller needs to be registered using Include
. For example:
// CMS API
type CMSController struct {
web.Controller
}
func (c *CMSController) URLMapping() {
c.Mapping("StaticBlock", c.StaticBlock)
c.Mapping("AllBlock", c.AllBlock)
}
// @router /staticblock/:key [get]
func (this *CMSController) StaticBlock() {
}
// @router /all/:key [get]
func (this *CMSController) AllBlock() {
}
The routers can then be registered in router.go
web.Include(&CMSController{})
Beego will parse the source code automatically when under dev mode.
The following routers will be supported:
- GET /staticblock/:key
- GET /all/:key
This is exactly same as registering by Router functions:
web.Router("/staticblock/:key", &CMSController{}, "get:StaticBlock")
web.Router("/all/:key", &CMSController{}, "get:AllBlock")
If you do not use URLMapping
Beego will find the function by reflection, otherwise Beego will find the function with the must faster interface
.
Beego supports automatic injection of http request parameters as method arguments, and method return values as http responses. For example, defining the following controller method:
// @router /tasks/:id
func (c *TaskController) MyMethod(id int, field string) (map[string]interface{}, error) {
if u, err := getObjectField(id, field); err == nil {
return u, nil
} else {
return nil, context.NotFound
}
}
will automatically route the http parameters id and field (i.e. /tasks/5?field=name
) to the correct method parameters, and will render the method return value as JSON. If the method returns an error it will be rendered as an http status code.
If the parameter does not exist in the http request it will be passed to the method as the zero value for that parameter, unless that parameter is marked as 'required' using annotations. This will return an error without calling the method.
For more information, see Parameters
The method expression router is to register routers by providing a controller method expresion. If the receiver of
the controller method is a non-pointer type, then you can pass method expression as pkg.controller.method
. If the receiver
of method is a pointer, then you need to pass method expression as (*pkg.controller).method
. However, if you register router in
the same package as controller, then you don't need to provide pkg
.
// Here are some examples:
type BaseController struct {
web.Controller
}
func (b BaseController) Ping() {
b.Data["json"] = "pong"
b.ServeJSON()
}
func (b *BaseController) PingPointer() {
b.Data["json"] = "pong_pointer"
b.ServeJSON()
}
func main() {
web.CtrlGet("/ping", BaseController.Ping)
web.CtrlGet("/ping_pointer", (*BaseController).PingPointer)
web.Run()
}
There are many other Method Expression Routers:
- web.CtrlGet(router, pkg.controller.method)
- web.CtrlPost(router, pkg.controller.method)
- web.CtrlPut(router, pkg.controller.method)
- web.CtrlPatch(router, pkg.controller.method)
- web.CtrlHead(router, pkg.controller.method)
- web.CtrlOptions(router, pkg.controller.method)
- web.CtrlDelete(router, pkg.controller.method)
- web.CtrlAny(router, pkg.controller.method)
It also provides namespace functions:
- web.NSCtrlGet
- web.NSCtrlPost
- ......
//init namespace
ns :=
web.NewNamespace("/v1",
web.NSCond(func(ctx *context.Context) bool {
if ctx.Input.Domain() == "api.web.me" {
return true
}
return false
}),
web.NSBefore(auth),
web.NSGet("/notallowed", func(ctx *context.Context) {
ctx.Output.Body([]byte("notAllowed"))
}),
web.NSRouter("/version", &AdminController{}, "get:ShowAPIVersion"),
web.NSRouter("/changepassword", &UserController{}),
web.NSNamespace("/shop",
web.NSBefore(sentry),
web.NSGet("/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte("notAllowed"))
}),
),
web.NSNamespace("/cms",
web.NSInclude(
&controllers.MainController{},
&controllers.CMSController{},
&controllers.BlockController{},
),
),
)
//register namespace
web.AddNamespace(ns)
the code set out above supports the URL:
- GET /v1/changepassword
- POST /v1/changepassword
- GET /v1/shop/123
- GET /v1/cms/ maps to annotation routers in MainController, CMSController, BlockController
namespace supports filter, condition and nested namespace
namespace API:
-
NewNamespace(prefix string,...interface{})
Create a namespace object. The namespace object's methods are listed below. For compatibility with the gofmt tool is is recommend that these method names begin with
NS
. -
NSCond(cond namespaceCond)
if the namespaceCond returns true this namespace will be run.
-
NSBefore(filterList ...FilterFunc)
-
NSAfter(filterList ...FilterFunc)
For
BeforeRouter
andFinishRouter
filters. Multiple filters can be registered. -
NSInclude(cList ...ControllerInterface)
-
NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string)
-
NSGet(rootpath string, f FilterFunc)
-
NSPost(rootpath string, f FilterFunc)
-
NSDelete(rootpath string, f FilterFunc)
-
NSPut(rootpath string, f FilterFunc)
-
NSHead(rootpath string, f FilterFunc)
-
NSOptions(rootpath string, f FilterFunc)
-
NSPatch(rootpath string, f FilterFunc)
-
NSAny(rootpath string, f FilterFunc)
-
NSHandler(rootpath string, h http.Handler)
-
NSAutoRouter(c ControllerInterface)
-
NSAutoPrefix(prefix string, c ControllerInterface)
These are methods to set up routers equivilant to the basic routers.
-
NSNamespace(prefix string, params ...innnerNamespace)
Nested namespaces
ns := web.NewNamespace("/v1", web.NSNamespace("/shop", web.NSGet("/:id", func(ctx *context.Context) { ctx.Output.Body([]byte("shopinfo")) }), ), web.NSNamespace("/order", web.NSGet("/:id", func(ctx *context.Context) { ctx.Output.Body([]byte("orderinfo")) }), ), web.NSNamespace("/crm", web.NSGet("/:id", func(ctx *context.Context) { ctx.Output.Body([]byte("crminfo")) }), ), )
The methods below are for the*Namespace
object and are not recommended. They have the same functionality as methods with NS
, but are less elegant and harder to read.
-
Cond(cond namespaceCond)
if the namespaceCond return true will run this namespace.
-
Filter(action string, filter FilterFunc)
action represents which position to run ,
before
andafter
is two validate value -
Router(rootpath string, c ControllerInterface, mappingMethods ...string)
-
AutoRouter(c ControllerInterface)
-
AutoPrefix(prefix string, c ControllerInterface)
-
Get(rootpath string, f FilterFunc)
-
Post(rootpath string, f FilterFunc)
-
Delete(rootpath string, f FilterFunc)
-
Put(rootpath string, f FilterFunc)
-
Head(rootpath string, f FilterFunc)
-
Options(rootpath string, f FilterFunc)
-
Patch(rootpath string, f FilterFunc)
-
Any(rootpath string, f FilterFunc)
-
Handler(rootpath string, h http.Handler)
these functions are the same as mentioned earlier
-
Namespace(ns ...*Namespace)
More functions can be nested:
//APIS
ns :=
web.NewNamespace("/api",
//It should verify the encrypted request in the production using
web.NSCond(func(ctx *context.Context) bool {
if ua := ctx.Input.Request.UserAgent(); ua != "" {
return true
}
return false
}),
web.NSNamespace("/ios",
//CRUD Create, Read, Update and Delete
web.NSNamespace("/create",
// /api/ios/create/node/
web.NSRouter("/node", &apis.CreateNodeHandler{}),
// /api/ios/create/topic/
web.NSRouter("/topic", &apis.CreateTopicHandler{}),
),
web.NSNamespace("/read",
web.NSRouter("/node", &apis.ReadNodeHandler{}),
web.NSRouter("/topic", &apis.ReadTopicHandler{}),
),
web.NSNamespace("/update",
web.NSRouter("/node", &apis.UpdateNodeHandler{}),
web.NSRouter("/topic", &apis.UpdateTopicHandler{}),
),
web.NSNamespace("/delete",
web.NSRouter("/node", &apis.DeleteNodeHandler{}),
web.NSRouter("/topic", &apis.DeleteTopicHandler{}),
)
),
)
web.AddNamespace(ns)