Controller Style Router
Basic Usage
import "github.com/beego/beego/v2/server/web"
type UserController struct {
web.Controller
}
Adding methods:
import "github.com/beego/beego/v2/server/web"
type UserController struct {
web.Controller
}
func (u *UserController) HelloWorld() {
u.Ctx.WriteString("hello, world")
}
func main() {
web.AutoRouter(&UserController{})
web.Run()
}
Accessing URL http://127.0.0.1:8080/user/helloworld
:
Note that the methods inside the controller that handle http
requests must be public methods - i.e. initially capitalized and have no parameters and no return value. If your method does not meet this requirement, in most cases, a panic
will occur, e.g. if your method has parameters:
func (u *UserController) HelloWorldNoPtr(name string) {
u.Ctx.WriteString("don't work")
}
Note that the
HandleFunc
in functional style actually takes a*Context
parameter
Or:
func (u UserController) HelloWorldNoPtr() {
u.Ctx.WriteString("don't work")
}
The general convention is to use a pointer receiver, but this is not mandatory. For a discussion of receivers, seechoose receiver
Controller Name
In some of the smarter APIs, we use the Controller
name as a prefix, namespace, etc.
In general:
type CtrlNameController struct {
}
For example, if we define a UserController
, then the name of the Controller
is User
. If it is case-insensitive, then user
is also a legal name.
Then let’s say we define a BuyerRefundController
, then BuyerRefund
is the name, and when case insensitive, buyerrefund
is also the legal name.
AutoRouter
The routing rules resolved by AutoRouter
are determined by the value of RouterCaseSensitive
, the name of Controller
and the method name.
Where UserController
it’s name is User
and the method name is HelloWorld
. Then.
- If
RouterCaseSensitive
istrue
, thenAutoRouter
registers two routes,/user/helloworld/*
,/User/HelloWorld/*
. - If
RouterCaseSensitive
isfalse
, then one route will be registered,/user/helloworld/*
.
In summary, it is always correct to use all lowercase paths when using AutoRouter
.
AutoPrefix
AutoRouter
is internally based on the implementation of AutoPrefix
, so to speak, the name of the Controller
, which is the registered prefix (prefix).
Example:
import (
"github.com/beego/beego/v2/server/web"
)
type UserController struct {
web.Controller
}
func (u *UserController) HelloWorld() {
u.Ctx.WriteString("Hello, world")
}
func main() {
// get http://localhost:8080/api/user/helloworld
// you will see return "Hello, world"
ctrl := &UserController{}
web.AutoPrefix("api", ctrl)
web.Run()
}
Accessinghttp://localhost:8080/api/user/helloworld
and then see “Hello, world”。
Similar to AutoRoute
:
If
RouterCaseSensitive
istrue
, thenAutoPrefix
registers two routes,api/user/helloworld/*
,api/User/HelloWorld/*
.If
RouterCaseSensitive
isfalse
, then one route will be registered,api/user/helloworld/*
.
Here we can summarize the rules of a general nature: When we use AutoPrefix
, the registered route matches the pattern prefix/ctrlName/methodName
.
Manual
If we don’t want to use AutoRoute
or AutoPrefix
to register routes, because both of them depend on the name of the Controller
and also on the name of the method. We may expect more flexibility.
In this scenario, we might consider say, using manual registration to register routes one by one.
In v2.0.2 we introduced a completely new way of registration:
import (
"github.com/beego/beego/v2/server/web"
)
type UserController struct {
web.Controller
}
func (u *UserController) GetUserById() {
u.Ctx.WriteString("GetUserById")
}
func (u *UserController) UpdateUser() {
u.Ctx.WriteString("UpdateUser")
}
func (u *UserController) UserHome() {
u.Ctx.WriteString("UserHome")
}
func (u *UserController) DeleteUser() {
u.Ctx.WriteString("DeleteUser")
}
func (u *UserController) HeadUser() {
u.Ctx.WriteString("HeadUser")
}
func (u *UserController) OptionUsers() {
u.Ctx.WriteString("OptionUsers")
}
func (u *UserController) PatchUsers() {
u.Ctx.WriteString("PatchUsers")
}
func (u *UserController) PutUsers() {
u.Ctx.WriteString("PutUsers")
}
func main() {
// get http://localhost:8080/api/user/123
web.CtrlGet("api/user/:id", (*UserController).GetUserById)
// post http://localhost:8080/api/user/update
web.CtrlPost("api/user/update", (*UserController).UpdateUser)
// http://localhost:8080/api/user/home
web.CtrlAny("api/user/home", (*UserController).UserHome)
// delete http://localhost:8080/api/user/delete
web.CtrlDelete("api/user/delete", (*UserController).DeleteUser)
// head http://localhost:8080/api/user/head
web.CtrlHead("api/user/head", (*UserController).HeadUser)
// patch http://localhost:8080/api/user/options
web.CtrlOptions("api/user/options", (*UserController).OptionUsers)
// patch http://localhost:8080/api/user/patch
web.CtrlPatch("api/user/patch", (*UserController).PatchUsers)
// put http://localhost:8080/api/user/put
web.CtrlPut("api/user/put", (*UserController).PutUsers)
web.Run()
}
It is important to note that our new registration method, requires that when we pass in the method, we pass in (*YourController).MethodName
. This is because of a Go language feature that requires that if you wish to get the method when the receiver is a pointer, then you should use (*YourController)
.
Without pointer:
import (
"github.com/beego/beego/v2/server/web"
)
type UserController struct {
web.Controller
}
func (u UserController) GetUserById() {
u.Ctx.WriteString("GetUserById")
}
func (u UserController) UpdateUser() {
u.Ctx.WriteString("UpdateUser")
}
func (u UserController) UserHome() {
u.Ctx.WriteString("UserHome")
}
func (u UserController) DeleteUser() {
u.Ctx.WriteString("DeleteUser")
}
func (u UserController) HeadUser() {
u.Ctx.WriteString("HeadUser")
}
func (u UserController) OptionUsers() {
u.Ctx.WriteString("OptionUsers")
}
func (u UserController) PatchUsers() {
u.Ctx.WriteString("PatchUsers")
}
func (u UserController) PutUsers() {
u.Ctx.WriteString("PutUsers")
}
func main() {
// get http://localhost:8080/api/user/123
web.CtrlGet("api/user/:id", UserController.GetUserById)
// post http://localhost:8080/api/user/update
web.CtrlPost("api/user/update", UserController.UpdateUser)
// http://localhost:8080/api/user/home
web.CtrlAny("api/user/home", UserController.UserHome)
// delete http://localhost:8080/api/user/delete
web.CtrlDelete("api/user/delete", UserController.DeleteUser)
// head http://localhost:8080/api/user/head
web.CtrlHead("api/user/head", UserController.HeadUser)
// patch http://localhost:8080/api/user/options
web.CtrlOptions("api/user/options", UserController.OptionUsers)
// patch http://localhost:8080/api/user/patch
web.CtrlPatch("api/user/patch", UserController.PatchUsers)
// put http://localhost:8080/api/user/put
web.CtrlPut("api/user/put", UserController.PutUsers)
web.Run()
}
We recommend that if you use this family of methods, then you should choose to use a structured receiver so that the code looks much cleaner.
The extra thing to notice is the CtrlAny
method, which means that any http
method can be handled.
Historical Methods
Historically, we have registered routes in such a way:
func main() {
ctrl := &MainController{}
// we register the path / to &MainController
// if we don't pass methodName as third param
// web will use the default mappingMethods
// GET http://localhost:8080 -> Get()
// POST http://localhost:8080 -> Post()
// ...
web.Router("/", ctrl)
// GET http://localhost:8080/health => ctrl.Health()
web.Router("/health", ctrl, "get:Health")
// POST http://localhost:8080/update => ctrl.Update()
web.Router("/update", ctrl, "post:Update")
// support multiple http methods.
// POST or GET http://localhost:8080/update => ctrl.GetOrPost()
web.Router("/getOrPost", ctrl, "get,post:GetOrPost")
// support any http method
// POST, GET, PUT, DELETE... http://localhost:8080/update => ctrl.Any()
web.Router("/any", ctrl, "*:Any")
web.Run()
}
We no longer recommend using this approach because readability and maintainability are not good. Especially when refactoring for method renaming, it is easy to make mistakes.