beego 0.9.0 中智能路由AutoRouter的使用方法及源码解读
了解beego的开发者肯定知道,beego的路由设计来源于sinatra,原来是不支持自动路由的,每一个路由都要自己配置的,如:
type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Ctx.WriteString("hello world")
} func main() {
beego.Router("/", &MainController{})
beego.Run()
}
beego.Controller 提供所有的restful方法,Get,Post,Delete等方法,通过重写这些方法,已响应客户端不同的请求方式。
用过Node.js的同学,肯定觉得很熟悉,拿最常用的express框架来说,路由的定义方式也大抵是这样的,如:
app.get('/', index); var index = function(req,res){
res.send("Hello,express");
};
有的同学肯定觉得为什么像php,java,asp这种服务器语言就不需要这样定义路由,自然会根据请求url,判断脚本path,进项执行并返回。
其实这些也是需要的,只不过这个工作交给了服务器软件来解决,如Apache,Nginx,IIS,Tomcat等。由这些软件提供Http服务,脚本程序本身更专注于服务的逻辑。
而如Node.js,Golang这种语言,是由自生提供Http服务,并监听某一端口。所以通过查看服务器响应Header可以看出,此类语言的server显示为express,beegoserver,而大部分网站返回头的server为Nginx,Apache等。当然,Golang,Node.js也可以通过反向代理功能(如Nginx),使真正与客户端打交道的变为这些反向代理软件,但注意的是,这并不代表Node.js等的Http服务和路由调度不工作了,他们依然接受来自反向代理软件的Http请求,并作出响应。
好了,扯了这么多,那0.9.0版本的beego提供的智能路由究竟是怎样呢?
先看一段,示例代码:
package main import (
"github.com/astaxie/beego"
"myapp/beego/controllers"
)
func main() {
beego.AutoRouter(&controllers.UserController{})
beego.AutoRouter(&controllers.PageController{})
//........
beego.Run()
}
控制器代码如下:
package controllers
import (
"github.com/astaxie/beego"
) type UserController struct {
beego.Controller
} type PageController struct {
beego.Controller
}
func (this *UserController) Add() {
this.Ctx.WriteString("/user/add")
}
func (this *PageController) About() {
this.Ctx.WriteString("/page/about")
}
有了这个AutoRouter,便不需要像以前那样逐一注册了,访问/user/add 调用UserController的Add方法,访问/page/about调用PageController的About方法。
这里需要稍微提醒两点:
1.控制器struct极其下func都必须以大写字母开头,因为在Golang里默认大写开头的为public,小写开头的为private,私有的内容无法被包外访问。
2.在使用了AutoRouter之后,原来的Router方法依然是有效的,可以继续使用。
好了,AutoRouter的使用就先介绍这里,0.9.0版本的beego还是更新和添加了不少功能的,在这里感谢Astaxie为golang项目所做的努力。
beego具体全面的使用,大家如果感兴趣的话,我以后可以抽个时间做个完成的介绍。
接下来我们来具体看看AutoRouter是怎么工作的,源码走起:
func AutoRouter(c ControllerInterface) *App {
BeeApp.AutoRouter(c)
return BeeApp
}
func (app *App) AutoRouter(c ControllerInterface) *App {
app.Handlers.AddAuto(c)
return app
}
type App struct {
Handlers *ControllerRegistor
}
func (p *ControllerRegistor) AddAuto(c ControllerInterface) {
p.enableAuto = true
reflectVal := reflect.ValueOf(c)
rt := reflectVal.Type()
ct := reflect.Indirect(reflectVal).Type()
firstParam := strings.ToLower(strings.TrimSuffix(ct.Name(), "Controller"))
if _, ok := p.autoRouter[firstParam]; ok {
return
} else {
p.autoRouter[firstParam] = make(map[string]reflect.Type)
}
for i := ; i < rt.NumMethod(); i++ {
p.autoRouter[firstParam][rt.Method(i).Name] = ct
}
}
type ControllerRegistor struct {
routers []*controllerInfo
fixrouters []*controllerInfo
enableFilter bool
filters []http.HandlerFunc
enableAfter bool
afterFilters []http.HandlerFunc
enableUser bool
userHandlers map[string]*userHandler
enableAuto bool
autoRouter map[string]map[string]reflect.Type //key:controller key:method value:reflect.type
}
if p.enableAuto {
if !findrouter {
for cName, methodmap := range p.autoRouter { if strings.ToLower(requestPath) == "/"+cName {
http.Redirect(w, r, requestPath+"/", )
return
} if strings.ToLower(requestPath) == "/"+cName+"/" {
requestPath = requestPath + "index"
}
if strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/") {
for mName, controllerType := range methodmap {
if strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/"+strings.ToLower(mName)) {
//execute middleware filters
if p.enableFilter {
for _, filter := range p.filters {
filter(w, r)
if w.started {
return
}
}
}
//parse params
otherurl := requestPath[len("/"+cName+"/"+strings.ToLower(mName)):]
if len(otherurl) > {
plist := strings.Split(otherurl, "/")
for k, v := range plist[:] {
params[strconv.Itoa(k)] = v
}
}
//Invoke the request handler
vc := reflect.New(controllerType) //call the controller init function
init := vc.MethodByName("Init")
in := make([]reflect.Value, )
ct := &Context{ResponseWriter: w, Request: r, Params: params, RequestBody: requestbody} in[] = reflect.ValueOf(ct)
in[] = reflect.ValueOf(controllerType.Name())
init.Call(in)
//call prepare function
in = make([]reflect.Value, )
method := vc.MethodByName("Prepare")
method.Call(in)
method = vc.MethodByName(mName)
method.Call(in)
//if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf
if EnableXSRF {
method = vc.MethodByName("XsrfToken")
method.Call(in)
if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" ||
(r.Method == "POST" && (r.Form.Get("_method") == "delete" || r.Form.Get("_method") == "put")) {
method = vc.MethodByName("CheckXsrfCookie")
method.Call(in)
}
}
if !w.started {
if AutoRender {
method = vc.MethodByName("Render")
method.Call(in)
}
}
method = vc.MethodByName("Finish")
method.Call(in)
//execute middleware filters
if p.enableAfter {
for _, filter := range p.afterFilters {
filter(w, r)
if w.started {
return
}
}
}
method = vc.MethodByName("Destructor")
method.Call(in)
// set find
findrouter = true
}
}
}
}
}
}
这里可以看到最先有一个判断if !findrouter 即如果没有找到路由匹配,才会进行智能路由匹配,所以Router的优先级是比AutoRouter要高的。
在这里再次用到了reflect,这里 ct := &Context{ResponseWriter: w, Request: r, Params: params, RequestBody: requestbody} 即获取http请求上下文,通过method.Call(in),
将http请求参数传给Controller内的相对应的方法。
不难看出,做了多步处理,有点类似PHP的钩子(hooks),依次经过控制器init方法->Prepare->XsrfToken->Render->Finish->Destructor等处理。
在最后set findrouter 为true,如果在这里仍没有匹配到router,接下来就404了
if !findrouter {
if h, ok := ErrorMaps[""]; ok {
w.status =
h(w, r)
} else {
http.NotFound(w, r)
}
}
所以beego的设计还是比较严谨且有效率的,在这里在此代表广大Golang初学者感谢谢大。
额,第一次写Golang的文章,感觉力不从心,说了一堆废话,忘园友们见谅!
beego 0.9.0 中智能路由AutoRouter的使用方法及源码解读的更多相关文章
- HttpServlet中service方法的源码解读
前言 最近在看<Head First Servlet & JSP>这本书, 对servlet有了更加深入的理解.今天就来写一篇博客,谈一谈Servlet中一个重要的方法-- ...
- RxJava2 中多种取消订阅 dispose 的方法梳理( 源码分析 )
Github 相关代码: Github地址 一直感觉 RxJava2 的取消订阅有点混乱, 这样也能取消, 那样也能取消, 没能系统起来的感觉就像掉进了盘丝洞, 迷乱… 下面说说这几种情况 几种取消的 ...
- Spark-1.6.0中的Sort Based Shuffle源码解读
从Spark-1.2.0开始,Spark的Shuffle由Hash Based Shuffle升级成了Sort Based Shuffle.即Spark.shuffle.manager从Hash换成了 ...
- C#调用接口注意要点 socket,模拟服务器、客户端通信 在ASP.NET Core中构建路由的5种方法
C#调用接口注意要点 在用C#调用接口的时候,遇到需要通过调用登录接口才能调用其他的接口,因为在其他的接口需要在登录的状态下保存Cookie值才能有权限调用, 所以首先需要通过调用登录接口来保存c ...
- AFNetworking 3.0 源码解读 总结(干货)(下)
承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...
- AFNetworking 3.0 源码解读 总结(干货)(上)
养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...
- AFNetworking 3.0 源码解读(十一)之 UIButton/UIProgressView/UIWebView + AFNetworking
AFNetworking的源码解读马上就结束了,这一篇应该算是倒数第二篇,下一篇会是对AFNetworking中的技术点进行总结. 前言 上一篇我们总结了 UIActivityIndicatorVie ...
- AFNetworking 3.0 源码解读(十)之 UIActivityIndicatorView/UIRefreshControl/UIImageView + AFNetworking
我们应该看到过很多类似这样的例子:某个控件拥有加载网络图片的能力.但这究竟是怎么做到的呢?看完这篇文章就明白了. 前言 这篇我们会介绍 AFNetworking 中的3个UIKit中的分类.UIAct ...
- AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager
让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心. 前言 AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控 ...
随机推荐
- Socket 两平台互相 通信 .NET
两个平台互相通信,对方发送数据过来,我方接收数据,对数据进行处理后发送结果给对方,对方进行相应的操作. 首页,我方开启服务监听: Socket socket = new Socket(AddressF ...
- js中的相等与不等运算
如果其中一个操作数的类型为 Boolean ,那么,首先将它转换为数字类型,false 转换为 0, true 将转换为 1. 如果其中一个操作数的类型是字符串,另外一个为数字类型,那么,将字符串转换 ...
- Cocos2d-JS中的精灵菜单和图片菜单
精灵菜单的菜单项类是cc.MenuItemSprite,图片菜单的菜单项类是cc.MenuItemImage.由于cc.MenuItemImage继承于cc.MenuItemSprite,所以图片菜单 ...
- 【学习笔记】【C语言】指向结构体的指针
1.指向结构体的指针的定义 struct Student *p; 2.利用指针访问结构体的成员 1> (*p).成员名称 2> p->成员名称 3.代码 #include < ...
- AMQ学习笔记 - 16. 确认机制的测试
概述 对Acknowledge机制进行测试. 此处的测试是针对Consumer的确认设计的:对于Producer的确认是透明的,无法提供测试. 测试实例 设计demo,测试三种确认机制. 测试机制 测 ...
- [javascript|基本概念]学习笔记
1/语法 a.区分大小写 b.标识符(首字符必须是字母/"_"/"$",其他可为字母/"_"/"$"/数字,不能用关键字 ...
- 汇编中PTR常见的几种用法
汇编中PTR的用法确实是令人比较头疼的,我特意搜集了一些PTR的应用实例,可以从例子中揣摩出规律: 1.MOV WORD PTR [DI],OFFSET BUF1 2.SUB BYTE ...
- Request.IsLocal与Request.Url.IsLoopback的区别
均在服务器上访问时: http://localhost:17810 Request.IsLocal => trueRequest.Url.IsLoopback => true http:/ ...
- win7 php5.5 apache 源码安装 imagick扩展
最近公司项目有用到php 的imagick,折腾了好长时间才把扩展装上,最主要的就是最新的不一定是最合适的,最开始一直找最新包安装,一直都不成功,经过google了好长时间,终于找到一个有用的,灵机一 ...
- 私人定制自己的linux小系统
私人定制自己的linux小系统 一.前言 linux操作系统至1991.10.5号诞生以来,就源其开源性和自由性得到了很多技术大牛的青睐,每个linux爱好者都为其贡献了自己的一份力,不管是在 ...