This is a creation in Article, where the information may have evolved or changed.
Beego is a well-known Golang WEB framework based on the MVC model, which supports automatic routing and restful, and config,cache,session,orm modules can be used directly. The most important thing in MVC is routing. It implements the mapping from the Web request to the corresponding controller method. Typically, a route consists of three parts: adding Rules , parsing rules , and matching rules . Just I was divided into three parts to analyze the Beego routing implementation mechanism.
This article is the analysis of adding routing rules, where do you start? Beego has an open source forum system Wetalk, that is, golanghome implementation, from his routing began to look.
The Wetalk routing is very long and there are many controllers that need to be registered. From a simple perspective, I wetalk.go#l79:
posts := new(post.PostListRouter)beego.Router("/", posts, "get:Home")beego.Router("/:slug(recent|best|cold|favs|follow)", posts, "get:Navs")beego.Router("/category/:slug", posts, "get:Category")beego.Router("/topic/:slug", posts, "get:Topic;post:TopicSubmit")
General Routing
The method of registering a route is beego.Router(...)
that the parameter is the URL rule, the Controller object, and his internal counterpart. How to use this method, you can refer to the official documents. How do you do it? Keep looking down, Beego.go:
func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App {BeeApp.Handlers.Add(rootpath, c, mappingMethods...)return BeeApp}
Hehe, the method falls on BeeApp.Hanlders
. BeeApp.Hanlders
is an *ControllerRegistor
instance, corresponding to the Add
method in Router.go:
Func (P *controllerregistor) Add (pattern string, C Controllerinterface, Mappingmethods ... string) {reflectval: = reflect. ValueOf (c) T: = reflect. Indirect (Reflectval). Type () Methods: = Make (map[string]string) if Len (mappingmethods) > 0 {semi: = strings. Split (Mappingmethods[0], ";") For _, V: = Range Semi {colon: = strings. Split (V, ":") If Len (colon)! = 2 {Panic ("method mapping format is invalid")}comma: = Strings. Split (Colon[0], ",") for _, M: = Range Comma {if _, OK: = Httpmethod[strings. ToUpper (m)]; m = = "*" | | OK {if Val: = Reflectval.methodbyname (Colon[1]); Val. IsValid () {methods[strings. ToUpper (m)] = Colon[1]} else {panic ("'" + colon[1] + "' method doesn ' t exist in the controller" + T.name ())}}} else {panic (V + "is an invalid method mapping. Method doesn ' t exist "+ m)}}}}route: = &controllerinfo{}route.pattern = Patternroute.methods = Methodsroute.routerty PE = Routertypebeegoroute.controllertype = Tif len (methods) = = 0 {For _, M: = Range HttpMethod {p.addtorouter (M, pattern, RouTE)}} else {for k, _: = Range methods {if k = = ' * ' {for _, M: = Range HttpMethod {p.addtorouter (M, Pattern, route)}} else {P.addtorouter (k, pattern, route)}}}}
A long, 1.1-point look:
The first step is to get the reflection type of the controller reflect.Type
.
The second step, parsing mappingMethods
, is beego.Router(...)
the third parameter of the above code, for example get:Topic;post:TopicSubmit
. The literal guess is the controller method name that corresponds to the HTTP request, like GET-postlistrouter.topic (). Semicolons divide multiple HTTP requests, colon-split HTTP requests, and corresponding controller methods. HTTPMETHOD
limit the supported HTTP request mode, not the normal panic. *
means to match all of them HTTPMETHOD
. Use reflection to obtain a corresponding method to determine whether it is valid.
The third step, build controllerInfo{}
, and add to the route. pattern
is the incoming URL rule, which has not yet been resolved. methods
is the mapping of the HTTP request mode to the Controller method in the parsed route parameters. Here's one routerTypeBeego
, the identity controllerInfo{}
is a generic route. There are routerTypeRESTFul
also routerTypeHandler
two kinds, which will be explained below.
Next, just look at what p.addToRouter(...)
is a! Router.go:
func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerInfo) {if !RouterCaseSensitive {pattern = strings.ToLower(pattern)}if t, ok := p.routers[method]; ok {t.AddRouter(pattern, r)} else {t := NewTree()t.AddRouter(pattern, r)p.routers[method] = t}}
Finally saw--the NewTree()
routing tree. The collection of all routing rules is actually one map[http_method]*Tree
. For the implementation of the routing tree, we will read the next article in detail.
RESTful Routing
Beego There's a way beego.RESTRouter(...)
. I thought this method was a RESTful type of routing, looking at source code discovery, or called beego.Router(...)
. Looking for a bit, routerTypeRESTFul
type of route that was originally in Router.go beego.AddMethod(...)
:
func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) {if _, ok := HTTPMETHOD[strings.ToUpper(method)]; method != "*" && !ok {panic("not support http method: " + method)}route := &controllerInfo{}route.pattern = patternroute.routerType = routerTypeRESTFulroute.runfunction = fmethods := make(map[string]string)if method == "*" {for _, val := range HTTPMETHOD {methods[val] = val}} else {methods[strings.ToUpper(method)] = strings.ToUpper(method)}route.methods = methodsfor k, _ := range methods {if k == "*" {for _, m := range HTTPMETHOD {p.addToRouter(m, pattern, route)}} else {p.addToRouter(k, pattern, route)}}}
Also generates a controllerInfo{}
commit to the routing tree. The difference router.runfunction
is, not the reflection type of the controller, is a function type FilterFunc
. So where does this restful route work?
beego.Get(...)
It is beego.AddMethod("get",...)
. Similar beego.Post(...)
, and beego.Put(...)
so on. In other words, this is a class of routing that receives a function as a method for routing rules, rather than a controller.
HTTP Handler Routing
Beego also has a routerTypeHandler
type of route, adding a method in beego.Handler(...)
Router.go:
func (p *ControllerRegistor) Handler(pattern string, h http.Handler, options ...interface{}) {route := &controllerInfo{}route.pattern = patternroute.routerType = routerTypeHandlerroute.handler = hif len(options) > 0 {if _, ok := options[0].(bool); ok {pattern = path.Join(pattern, "?:all")}}for _, m := range HTTPMETHOD {p.addToRouter(m, pattern, route)}}
The generated controllerInfo{}
time is used http.Handler
, saved in the router.handler
field. And the following is the way the HTTP request for this route is set to all supported methods.
Automatic routing
Automatic routing is an added hassle in order to simplify the registration of routes with controllers. According to the structure of the controller to automatically add rules, the specific place in the Router.go:
Func (P *controllerregistor) addautoprefix (prefix string, c controllerinterface) {reflectval: = reflect. ValueOf (c) RT: = Reflectval.type () CT: = reflect. Indirect (Reflectval). Type () Controllername: = Strings. Trimsuffix (Ct. Name (), "Controller") for I: = 0; I < Rt. Nummethod (); i++ {if!utils. Inslice (Rt. Method (i). Name, Exceptmethod) {route: = &controllerinfo{}route.routertype = Routertypebeegoroute.methods = map[string]string {"*": RT. Method (i). Name}route.controllertype = Ctpattern: = path. Join (prefix, strings. ToLower (controllername), strings. ToLower (Rt. Method (i). Name), "*") Patterninit: = path. Join (prefix, controllername, Rt. Method (i). Name, "*") Patternfix: = path. Join (prefix, strings. ToLower (controllername), strings. ToLower (Rt. Method (i). Name) Patternfixinit: = path. Join (prefix, controllername, Rt. Method (i). Name) Route.pattern = Patternfor _, M: = Range HttpMethod {p.addtorouter (M, pattern, route) P.addtorouter (M, Patterninit, RO Ute) P.addtorouter (M, Patternfix, route) P.addtorouter (m, patterNfixinit, Route)}}}
Here the pattern is spliced according to the controller's method. The controller's built-in method is not processed first exceptMethod
, and then, based on the controller's name and method, the case-sensitive registration prefix/controller/method/*
and prefix/controller/method
Two rules to all HTTP request methods.
Final
In general, the process of adding a route, summed up is added controllerInfo{}
to the *Tree
. controllerInfo{}
The structure is:
type controllerInfo struct {pattern stringcontrollerType reflect.Typemethods map[string]stringhandler http.Handlerrunfunction FilterFuncrouterType int}
As you can see, at this point the pattern--routing rule--has not yet been parsed. Only the method of HTTP request and corresponding call is recorded in the map of methods, or the method function of the call is saved directly in handler or runfunction.
So next, let's read the following controllerInfo{}
procedure to add to *Tree
. Since it is a routing tree, the hierarchy rules, node content, are important details.
Notice: This article is based on Beego v1.4.3