1、介绍
beego专注app软件定制开发是一个使用来开发WEB引用的GoWeb框架,专注app软件定制开发该框架起始于2012年,专注app软件定制开发由一位中国的程序员编专注app软件定制开发写并进行公开,专注app软件定制开发其目的就是为大家提供专注app软件定制开发一个高效率的web专注app软件定制开发应用开发框架。
1)特性
(1)简单化:支持,MVC模型;可以使用bee专注app软件定制开发工具来提高开发效率,专注app软件定制开发比如监控代码修改进行热编译,专注app软件定制开发自动化测试代码,专注app软件定制开发以及自动化打包部署等丰富的开发调试功能。
(2)智能化:beego封装了路由模块,支持智能路由,智能监控,并可以监控内存消耗,CPU使用以及goroutine的运行状况,方便开发者对线上应用进行监控分析。
(3)模块化:beego根据功能对代码进行节耦封装,形成了Session,Cache,Log,配置解析,性能监控,上下文操作,ORM等独立的模块,方便开发者进行使用。
(4)高性能:beego采用Go原生的http请求,goroutine的并发效率应付大流量的Web应用和API应用。
2)安装
go get .com/astaxie/beego (下载安装)
git config --global http.sslVerify false (提示https不支持时使用)
go get -u github.com/astaxie/beego (升级)
创建一个beego项目
- package main
-
- import "github.com/astaxie/beego"
-
- func main() {
-
- beego.Info("第一个beego案例")
-
- beego.Run("localhost:8080")
- }
go build -o hello hello.go
./hello
访问浏览器http://localhost:8080
3)bee工具
协助Beego框架开发项目时进行创建项目,运行项目,热部署等相关的项目管理的工具。安装完之后,bee 可执行文件默认存放在 $GOPATH/bin 里面,所以您需要把 $GOPATH/bin 添加到您的环境变量中
go get github.com/beego/bee (bee工具安装)
bee new <项目名> 创建一个新的项目
bee api <项目名> 开发 API 应用
bee run 命令是监控 beego 的项目
bee pack 用来打包
beego项目结构:
2、beego程序流程分析
Go语言执行的时候是main包下面的init函数、main函数依次执行。因此,先找到main.go文件。
首先,import导入了两个包,一个是routers,一个是beego。而在routers包前面,可以看到有一个“_”,这表明是引入routers包,并执行init方法。 然后到routers包下看到init方法:
router函数的功能是映射 URL 到 controller,第一个参数是 URL (用户请求的地址),这里注册的是 /,也就是访问的不带任何参数的 URL,第二个参数是对应的 Controller,即将把请求分发到那个控制器来执行相应的逻辑。
现在去这里设置的MainController中去看一下有什么方法:
MainController结构体及函数声明在default.go文件中。而这里就看到一个Get方法,方法中有三行代码。
一个get请求到了后台以后,什么请求参数都没有,就会被“/”拦截,执行到MainController中的代码,因为是get请求,所以这里自动找到Get函数并进行执行。
在get函数里面,有三句代码,前两句c.Data[]= ""表示设置返回的数据字段及内容,最后一句c.TplName表示设置处理该请求指向某个模板文件,这里指向了index.tpl。
解释:模板文件在views下面,通常的页面都是使用静态的html+css+js等这些静态代码来进行页面的布局,页面效果控制等,而把页面的数据使用变量表示,这样,在进行页面展示的时候,就能够自动的填充页面里面的变量的值;这些静态的代码文件统称为模板文件。每个模板文件就是像一个模板一样,样式效果都固定,只是根据数据不一样进行渲染和展示。
init方法分析完毕后,程序会继续往下执行,就到了main函数,在main函数中执行:beego.Run()代码。分析一下Run代码的逻辑,在Run方法内部,主要做了几件事:
- 解析配置文件,也就是我们的app.conf文件,比如端口,应用名称等信息。
- 检查是否开启session,如果开启session,就会初始化一个session对象。
- 是否编译模板,beego框架会在项目启动的时候根据配置把views目录下的所有模板进行预编译,然后存放在map中,这样可以有效的提高模板运行的效率,不需要进行多次编译。
- 监听服务端口。根据app.conf文件中的端口配置,启动监听。
3、架构
1)控制层
(1)控制器:controllers
该目录是存放控制器文件的目录,所谓控制器就是控制应用调用哪些业务逻辑,由controllers处理完http请求以后,并负责返回给前端调用者。
beego.Controller 实现了接口 beego.ControllerInterface,该接口存在Init(ct *context.Context, childName string, app interface{})、Prepare()、Get()、Post()等等函数
(2)路由器:routers
所谓路由就是分发的意思,当前端浏览器进行一个http请求达到后台web项目时,必须要让程序能够根据浏览器的请求url进行不同的业务处理,从接收到前端请求到判断执行具体的业务逻辑的过程的工作,就由routers来实现。
在beego框架中,支持四种路由设置,他们分别是:**基础路由**,**固定路由**,**正则路由**和**自动路由**。
基础路由
直接通过beego.Get,beego.POST,beego.Head,beego.Delete等方法来进行路由的映射,常见的http请求方法操作有:GET,HEAD,PUT,POST,DELETE,OPTIONS等。
- GET路由
-
- beego.GET("",func)
-
- beego.Get("/",func(ctx *context.Context){
-
- ctx.Output.Body([]byte("hello world")) })
-
- POST路由:
-
- beego.POST("",func)
固定路由
固定路由也就是全匹配的路由
- beego.Router("/", &controllers.MainController{})
-
- beego.Router("/admin", &admin.UserController{})
-
- beego.Router("/admin/addpkg", &admin.AddController{})
正则路由
beego.Router("/api/?:id", &controllers.RController{})
默认匹配 //例如对于URL"/api/123"可以匹配成功,此时变量":id"值为"123"
beego.Router("/api/:id", &controllers.RController{})
默认匹配 //例如对于URL"/api/123"可以匹配成功,此时变量":id"值为"123",但URL"/api/"匹配失败
beego.Router("/api/:id([0-9]+)", &controllers.RController{})
自定义正则匹配 //例如对于URL"/api/123"可以匹配成功,此时变量":id"值为"123"
beego.Router("/user/:username([\\w]+)", &controllers.RController{})
正则字符串匹配 //例如对于URL"/user/astaxie"可以匹配成功,此时变量":username"值为"astaxie"
beego.Router("/download/*.*", &controllers.RController{})
*匹配方式 //例如对于URL"/download/file/api.xml"可以匹配成功,此时变量":path"值为"file/api", ":ext"值为"xml"
beego.Router("/download/ceshi/*", &controllers.RController{})
*全匹配方式 //例如对于URL"/download/ceshi/file/api.json"可以匹配成功,此时变量":splat"值为"file/api.json"
beego.Router("/:id:int", &controllers.RController{})
int 类型设置方式,匹配 :id为int 类型,框架帮你实现了正则 ([0-9]+)
beego.Router("/:hi:string", &controllers.RController{})
string 类型设置方式,匹配 :hi 为 string 类型。框架帮你实现了正则 ([\w]+)
beego.Router("/cms_:id([0-9]+).html", &controllers.CmsController{})
带有前缀的自定义正则 //匹配 :id 为正则类型。匹配 cms_123.html 这样的 url :id = 123
自定义路由
beego.Router("/api/list",&RestController{},"*:ListFood")
beego.Router("/api/create",&RestController{},"post:CreateFood")
beego.Router("/api/update",&RestController{},"put:UpdateFood")
beego.Router("/api/delete",&RestController{},"delete:DeleteFood")
(3)参数配置
beego 默认会解析当前应用下的 conf/app.conf 文件。 通过这个文件你可以初始化很多 beego 的默认参数:
- appname = beepkg
-
- httpaddr = "127.0.0.1"
-
- httpport = 9090
-
- runmode ="dev"
-
- autorender = false
-
- recoverpanic = false
-
- viewspath = "myview"
你也可以在配置文件中配置应用需要用的一些配置信息,例如下面所示的数据库信息:
- mysqluser = "root"
-
- mysqlpass = "rootpass"
-
- mysqlurls = "127.0.0.1"
-
- mysqldb = "beego"
那么你就可以通过如下的方式获取设置的配置信息:
- beego.AppConfig.String("mysqluser")
-
- beego.AppConfig.String("mysqlpass")
-
- beego.AppConfig.String("mysqlurls")
-
- beego.AppConfig.String("mysqldb")
runmode 参数
- runmode ="dev"
-
- [dev]
-
- httpport = 8080
-
- [prod]
-
- httpport = 8088
-
- [test]
-
- httpport = 8888
include参数
include "app2.conf"
(4)XSRF
防御CSRF安全问题
在应用配置文件中加上 enablexsrf 设定:
- enablexsrf = true
-
- xsrfkey = 61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o
-
- xsrfexpire = 3600
或者直接在 main 入口处这样设置:
- beego.EnableXSRF = true
-
- beego.XSRFKEY = "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o"
-
- beego.XSRFExpire = 3600 //过期时间,默认1小时
如果开启了 XSRF,那么 beego 的 Web 应用将对所有用户设置一个 _xsrf 的 cookie 值(默认过期 1 小时),如果 POST PUT DELET 请求中没有这个 cookie 值,那么这个请求会被直接拒绝。
在 Controller 中这样设置数据:
- func (this *HomeController) Get(){
- this.Data["xsrfdata"]=template.HTML(this.XSRFFormHTML()
- ) }
然后在模板中这样设置:
- <form action="/new_message" method="post">
- {{ .xsrfdata }}
- <input type="text" name="message"/> <input type="submit" value="Post"/>
- </form>
(5)数据处理
通过如下方式获取数据:
- GetString(key string) string
- GetStrings(key string) []string
- GetInt(key string) (int64, error)
- GetBool(key string) (bool, error)
- GetFloat(key string) (float64, error)
解析struct,把表单里的内容赋值到一个 struct 里:
定义 struct:
- type user struct {
- Id int `form:"-"`
- Name interface{} `form:"username"`
- Age int `form:"age"`
- Email string
- }
表单:
- <form id="user">
- 名字:<input name="username" type="text" />
- 年龄:<input name="age" type="text" />
- 邮箱:<input name="Email" type="text" />
- <input type="submit" value="提交" />
- </form>
Controller 里解析:
- func (this *MainController) Post() {
- u := user{}
- if err := this.ParseForm(&u);
- err != nil { //handle error }
- }
获取 Request Body 里的 JSON 或 XML 的数据
在配置文件里设置 copyrequestbody = true
- func (this *ObjectController) Post() {
- var ob models.Object
- var err error
- if err = json.Unmarshal(this.Ctx.Input.RequestBody, &ob); err == nil {
- objectid := models.AddOne(ob)
- this.Data["json"] = "{\"ObjectId\":\"" + objectid + "\"}"
- } else {
- this.Data["json"] = err.Error()
- }
- this.ServeJSON()
- }
(6)session控制
beego 中使用 session 相当方便,只要在 main 入口函数中设置如下:
beego.BConfig.WebConfig.Session.SessionOn = true
或者通过配置文件配置如下:
sessionon = true
session 有几个方便的方法:
-
- SetSession(name string, value interface{})
- GetSession(name string) interface{}
- DelSession(name string)
- SessionRegenerateID()
- DestroySession()
(7)过滤器
beego.InsertFilter(pattern string, position int, filter FilterFunc, params ...bool)
pattern 路由规则,可以根据一定的规则进行路由,如果你全匹配可以用 *
position 执行 Filter 的地方,五个固定参数如下,分别表示不同的执行过程
- BeforeStatic 静态地址之前
- BeforeRouter 寻找路由之前
- BeforeExec 找到路由之后,开始执行相应的 Controller 之前
- AfterExec 执行完 Controller 逻辑之后执行的过滤器
- FinishRouter 执行完逻辑之后执行的过滤器
filter filter 函数 type FilterFunc func(*context.Context)
2)model层
数据库查询操作,同时提供了 ORM 框架
(1)models.go
- package main
-
- import (
- "github.com/astaxie/beego/orm"
- )
-
- type User struct {
- Id int
- Name string
- Profile *Profile `orm:"rel(one)"` // 设置一对一的关系
- Post []*Post `orm:"reverse(many)"` // 设置一对多的反向关系
- }
-
- type Profile struct {
- Id int
- Age int16
- User *User `orm:"reverse(one)"` // 设置一对一反向关系(可选)
- }
-
- type Post struct {
- Id int
- Title string
- User *User `orm:"rel(fk)"` //设置一对多关系
- Tags []*Tag `orm:"rel(m2m)"`
- }
-
- type Tag struct {
- Id int
- Name string
- Posts []*Post `orm:"reverse(many)"` //设置多对多反向关系
- }
-
- func init() {
- // 需要在init中注册定义的model
- orm.RegisterModel(new(User), new(Post), new(Profile), new(Tag))
- }
打印数据库语句
- func main() {
- orm.Debug = true
- var w io.Writer
- ...
- // 设置为你的 io.Writer
- ...
- orm.DebugLog = orm.NewLog(w)
日志格式:
[ORM] - 时间 - [Queries/数据库名] - [执行操作/执行时间] - [SQL语句] - 使用标点 `,` 分隔的参数列表 - 打印遇到的错误
(2)数据库配置
- import _ "github.com/go-sql-driver/mysql"
- func init() {
- driverName := beego.AppConfig.String("driverName")
-
- //注册数据库驱动
- orm.RegisterDriver(driverName, orm.DRMySQL)
-
- //数据库连接
- user := beego.AppConfig.String("mysqluser")
- pwd := beego.AppConfig.String("mysqlpwd")
- host := beego.AppConfig.String("host")
- port := beego.AppConfig.String("port")
- dbname := beego.AppConfig.String("dbname")
-
- //dbConn := "root:yu271400@tcp(127.0.0.1:3306)/cmsproject?charset=utf8"
- dbConn := user + ":" + pwd + "@tcp(" + host + ":" + port + ")/" + dbname + "?charset=utf8"
-
- err := orm.RegisterDataBase("default", driverName, dbConn)
- if err != nil {
- util.LogError("连接数据库出错")
- return
- }
- util.LogInfo("连接数据库成功")
- }
(3)注册模型
- import "github.com/astaxie/beego/orm"
-
- type User struct { Id int Name string } func init(){
-
- orm.RegisterModel(new(User))
-
- }
RegisterModel 也可以同时注册多个 model
orm.RegisterModel(new(User), new(Profile), new(Post))
用表名前缀
orm.RegisterModelWithPrefix("prefix_", new(User))
(4)ORM接口
QueryTable
传入表名,或者 Model 对象,返回一个 QuerySeter
- o := orm.NewOrm()
-
- var qs orm.QuerySeter
-
- qs = o.QueryTable("user") // 如果表没有定义过,会立刻 panic
Using
切换为其他数据库
-
- orm.RegisterDataBase("db1", "mysql", "root:root@/orm_db2?charset=utf8") orm.RegisterDataBase("db2", "sqlite3", "data.db")
-
- o1 := orm.NewOrm()
-
- o1.Using("db1")
-
- o2 := orm.NewOrm()
-
- o2.Using("db2") // 切换为其他数据库以后 这个 Ormer 对象的其下的 api 调用都将使用这个数据库
Raw
Raw 函数,原生 SQL查询
- o := orm.NewOrm()
- var r orm.RawSeter
- r = o.Raw("UPDATE user SET name = ? WHERE name = ?", "testing", "slene")
- ids := []int{1, 2, 3}
- p.Raw("SELECT name FROM user WHERE id IN (?, ?, ?)", ids)
返回result对象
- res, err := o.Raw("UPDATE user SET name = ?", "your").Exec()
-
- if err == nil {
-
- num, _ := res.RowsAffected()
-
- fmt.Println("mysql row affected nums: ", num)
-
- }
Prepare
用于一次 prepare 多次 exec,以提高批量执行的速度。
- p, err := o.Raw("UPDATE user SET name = ? WHERE name = ?").Prepare()
- res, err := p.Exec("testing", "slene")
- res, err = p.Exec("testing", "astaxie")
- p.Close() // 别忘记关闭 statement
RowsToStruct
查询结果匹配到 struct 里
- type Options struct { Total int Found int }
-
- res := new(Options)
- nums, err := o.Raw("SELECT name, value FROM options_table").RowsToStruct(res, "name", "value")
- fmt.Println(res.Total)
- fmt.Println(res.Found)
RowToMap
查询结果匹配到 map 里
- res := make(orm.Params)
- nums, err := o.Raw("SELECT name, value FROM options_table").RowsToMap(&res, "name", "value")
Values
返回结果集的 key => value 值
- var maps []orm.Params
- num, err := o.Raw("SELECT user_name FROM user WHERE status = ?", 1).Values(&maps)
- if err == nil && num > 0 {
- fmt.Println(maps[0]["user_name"])
- }
(5)GURD
Read
- o := orm.NewOrm()
- user := User{Id: 1}
-
- err := o.Read(&user)
-
- if err == orm.ErrNoRows {
- fmt.Println("查询不到")
- } else if err == orm.ErrMissPK {
- fmt.Println("找不到主键")
- } else {
- fmt.Println(user.Id, user.Name)
- }
-
- Read 默认通过查询主键赋值,可以使用指定的字段进行查询:
-
- user := User{Name: "slene"}
- err = o.Read(&user, "Name")
Insert
- o := orm.NewOrm()
- var user User
- user.Name = "slene"
- user.IsActive = true
-
- id, err := o.Insert(&user)
- if err == nil {
- fmt.Println(id)
- }
InsertMulti
- users := []User{
- {Name: "slene"},
- {Name: "astaxie"},
- {Name: "unknown"},
- ...
- }
- successNums, err := o.InsertMulti(100, users)
Update
- o := orm.NewOrm()
- user := User{Id: 1}
- if o.Read(&user) == nil {
- user.Name = "MyName"
- if num, err := o.Update(&user); err == nil {
- fmt.Println(num)
- }
- }
- Update 默认更新所有的字段,可以更新指定的字段:
-
- // 只更新 Name
- o.Update(&user, "Name")
Delete
- o := orm.NewOrm()
- if num, err := o.Delete(&User{Id: 1}); err == nil {
- fmt.Println(num)
- }
3)VIEW层
模板语法
go 统一使用了 {{ 和 }} 作为左右标签
使用 . 来访问当前位置的上下文
使用 $ 来引用当前模板根级的上下文
使用 $var 来访问创建的变量
学习链接: