beego github地址:https://github.com/beego/beego
beego中文学习文档:http://beego.gocn.vip/beego/zh/developing/
bee工具是一个为了协助快速开发 beego 项目而创建的项目,通过 bee 可以很容易的进行 beego 项目的创建、开发等。
go get -u github.com/beego/bee/v2
$GOPATH/bin
目录下,所以需要把$GOPATH/bin
添加到环境变量中,才可以使用bee工具使用命令:go build -o bee.exe
执行生成bee.exe,在放到bin目录下,然后添加到环境变量中
bee version
查看是否安装成功new
命令是新建一个 Web 项目,我们在命令行下执行 bee new <项目名>
就可以创建一个新的项目。但是注意该命令必须在 $GOPATH/src
下执行。最后会在 $GOPATH/src
相应目录下生成如下目录结构的项目:
bee new myproject
[INFO] Creating application...
/gopath/src/myproject/
/gopath/src/myproject/conf/
/gopath/src/myproject/controllers/
/gopath/src/myproject/models/
/gopath/src/myproject/static/
/gopath/src/myproject/static/js/
/gopath/src/myproject/static/css/
/gopath/src/myproject/static/img/
/gopath/src/myproject/views/
/gopath/src/myproject/conf/app.conf
/gopath/src/myproject/controllers/default.go
/gopath/src/myproject/views/index.tpl
/gopath/src/myproject/main.go
13-11-25 09:50:39 [SUCC] New application successfully created!
myproject
├── conf
│ └── app.conf
├── controllers
│ └── default.go
├── main.go
├── models
├── routers
│ └── router.go
├── static
│ ├── css
│ ├── img
│ └── js
├── tests
│ └── default_test.go
└── views
└── index.tpl
8 directories, 4 files
上面的 new
命令是用来新建 Web 项目,不过很多用户使用 beego 来开发 API 应用。所以这个 api
命令就是用来创建 API 应用的,执行命令之后如下所示:
bee api apiproject
create app folder: /gopath/src/apiproject
create conf: /gopath/src/apiproject/conf
create controllers: /gopath/src/apiproject/controllers
create models: /gopath/src/apiproject/models
create tests: /gopath/src/apiproject/tests
create conf app.conf: /gopath/src/apiproject/conf/app.conf
create controllers default.go: /gopath/src/apiproject/controllers/default.go
create tests default.go: /gopath/src/apiproject/tests/default_test.go
create models object.go: /gopath/src/apiproject/models/object.go
create main.go: /gopath/src/apiproject/main.go
这个项目的目录结构如下:
apiproject
├── conf
│ └── app.conf
├── controllers
│ └── object.go
│ └── user.go
├── docs
│ └── doc.go
├── main.go
├── models
│ └── object.go
│ └── user.go
├── routers
│ └── router.go
└── tests
└── default_test.go
从上面的目录我们可以看到和 Web 项目相比,少了 static 和 views 目录,多了一个 test 模块,用来做单元测试的。
同时,该命令还支持一些自定义参数自动连接数据库创建相关 model 和 controller: bee api [appname] [-tables=""] [-driver=mysql] [-conn="root:
如果 conn 参数为空则创建一个示例项目,否则将基于链接信息链接数据库创建项目。
定义一个controller
type UserController struct {
web.Controller
}
func (c *UserController) Hello() {
c.Ctx.WriteString("user/hello")
}
注册路由:
beego.AutoRouter(&controllers.UserController{})
运行成功后,访问localhost:8080/user/hello
注意:处理请求的方法必须是公共方法,即首字母大写,并且不能有参数和返回值,否则会发生panic
方法的接收器推荐使用对应controller的指针类型,访问接口就是/(controller的名字)/方法名
例如,我们定义了一个UserController,里面有个Hello()方法,接口就是/user/hello
注意:接口的所有字母均为小写,例如HelloWorld()方法,接口是/helloworld
,而不是/helloWorld
beego.Get("/user/getuser", func(ctx *context.Context) {
ctx.WriteString("/user/getuser")
})
//beego.Post()
//beego.Delete()
优先级:自动注册路由 > 手动注册路由
func (c *UserController) Rest() {
id := c.GetString(":id")
c.Ctx.WriteString(id)
}
beego.Router("/rest/:id", &controllers.UserController{}, "get:Rest")
接收参数使用GetString()
方法,key必须要加:
注册路由时,第三个参数为"请求类型:方法名"
,使用逗号隔开,例如get,post:Rest
或者get:Rest1,post:Rest2
func (c *UserController) Login() {
username := c.GetString("username")
password := c.GetString("password")
if username == "admin" && password == "123456" {
c.Ctx.WriteString("登录成功,username:" + username)
} else {
c.Ctx.WriteString("登录失败")
return
}
}
使用GetString()
根据key获取请求参数
访问:localhost:8080/user/login?username=admin&password=123456
定义一个表单html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录title>
head>
<body>
<form action="/user/login" method="post">
<p>用户名:<input type="text" name="Username" >p>
<p>密码:<input type="text" name="Password">p>
<p><input type="submit" value="提交">p>
form>
body>
html>
定义ToLogin跳转到html
func (c *UserController) ToLogin() {
c.TplName = "login.html"
}
表单参数获取
func (c *UserController) Login() {
type User struct {
Username string
Password string
}
user := User{}
c.BindForm(&user)
fmt.Println(user)
c.Ctx.WriteString("登录成功,用户名:" + user.Username)
}
使用BindForm()
接收
根据表单参数定义对应结构体,变量名保持一致,如果不一致,使用form:表单参数名字
进行映射
Username string `form:"user"`
Password string `form:"pwd"`
<form action="/user/login" method="post">
<p>用户名:<input type="text" name="user" >p>
<p>密码:<input type="text" name="pwd">p>
<p><input type="submit" value="提交">p>
form>
路由注册:
beego.Router("/user/tologin", &controllers.UserController{}, "get,post:ToLogin")
beego.Router("/user/login", &controllers.UserController{}, "post:Login")
c.Redirect("/", 302)
使用Redirect()
进行重定向,第一个参数为请求路径,第二个参数为重定向的状态码302
定义404页面
beego.ErrorHandler("404", func(writer http.ResponseWriter, request *http.Request) {
t, _ := template.New("404.html").ParseFiles(beego.BConfig.WebConfig.ViewsPath + "/404.html")
data := make(map[string]interface{})
data["content"] = "page not found"
t.Execute(writer, data)
})
当出现404时会自动跳转到404.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404title>
head>
<body>
404<br>
{{.content}}
body>
html>
定义一个ErrorController
package controllers
import "github.com/beego/beego/v2/server/web"
type ErrorController struct {
web.Controller
}
func (c *ErrorController) Error404() {
c.Data["content"] = "404"
c.TplName = "404.html"
}
注册路由
beego.ErrorController(&controllers.ErrorController{})
注意:所有的函数方法都是以Error
开头
开启session
需要配置conf
func (c *UserController) Session() {
v := c.GetSession("asta")
if v == nil {
c.SetSession("asta", int(1))
c.Data["num"] = 0
} else {
c.SetSession("asta", v.(int)+1)
c.Data["num"] = v.(int)
}
c.TplName = "session.html"
}
beego.Router("/session", &controllers.UserController{}, "get:Session")
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>sessiontitle>
head>
<body>
页面访问次数:{{.num}}
body>
html>
cookie使用
创建一个cookie
func (c *UserController) PutCookie() {
// put something into cookie,set Expires time
c.Ctx.SetCookie("name", "web cookie", 10)
// web-example/views/hello_world.html
c.TplName = "cookie.html"
c.Data["name"] = "PutCookie"
_ = c.Render()
}
获取这个cookie
func (c *UserController) ReadCookie() {
// web-example/views/hello_world.html
c.TplName = "cookie.html"
c.Data["name"] = c.Ctx.GetCookie("name")
// don't forget this
_ = c.Render()
}
cookie加密
func (c *UserController) PutCookie() {
// put something into cookie,set Expires time
c.Ctx.SetSecureCookie("my-secret", "name", "web cookie", 10)
// web-example/views/hello_world.html
c.TplName = "cookie.html"
c.Data["name"] = "PutCookie"
_ = c.Render()
}
func (c *UserController) ReadCookie() {
// web-example/views/hello_world.html
c.TplName = "cookie.html"
c.Data["name"],_ = c.Ctx.GetSecureCookie("my-secret", "name")
// don't forget this
_ = c.Render()
}
beego采用了sha256
来作为加密算法,第一个参数是加密的密钥
使用orm需要以下步骤:
定义一个用户结构体:
// User结构体
type User struct {
ID int `orm:"column(id)"`
Name string `orm:"column(name)"`
}
注册:
// 注册对象模型
user := new(User)
orm.RegisterModel(user)
// 配置数据库连接信息
username:="root"
password:="123456"
host:="127.0.0.1"
port:=3306
charset:="utf8"
dbname:="beego-orm"
dataSource:=fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s",username,password,host,port,dbname,charset)
// 连接数据库
orm.RegisterDataBase("default", "mysql", dataSource)
o := orm.NewOrm()
注意:一定要导入使用数据库的驱动,例如使用mysql数据库
import _ "github.com/go-sql-driver/mysql"
如果想查看具体执行的sql语句,可以设置orm.Debug = true
,开启查询日志,在生产环境中不要开启,会严重影响性能。
完整代码:
package main
import (
"fmt"
"github.com/beego/beego/v2/client/orm"
_ "github.com/go-sql-driver/mysql"
)
// User结构体
type User struct {
ID int `orm:"column(id)"`
Name string `orm:"column(name)"`
}
func init() {
// 注册对象模型
user := new(User)
orm.RegisterModel(user)
// 配置数据库连接信息
username:="root"
password:="123456"
host:="127.0.0.1"
port:=3306
charset:="utf8"
dbname:="beego-orm"
dataSource:=fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s",username,password,host,port,dbname,charset)
// 连接数据库
orm.RegisterDataBase("default", "mysql", dataSource)
}
func main() {
// 开启debug调试
orm.Debug = true
// 生成表
orm.RunSyncdb("default", false, true)
// 创建一个映射对象
o := orm.NewOrm()
// 复制
user := new(User)
user.Name = "zhangsan"
// 添加数据到表中
o.Insert(user)
}
数据库注册方法:orm.RegisterDataBase()
参数:
// 参数1 数据库的别名,用来在 ORM 中切换数据库使用
// 参数2 driverName
// 参数3 对应的链接字符串
orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8")
// 参数4(可选) 设置最大空闲连接
// 参数5(可选) 设置最大数据库连接 (go >= 1.2)
maxIdle := 30
maxConn := 30
orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8", orm.MaxIdleConnections(maxIdle), orm.MaxOpenConnections(maxConn))
注意:ORM 要求必须要注册一个default
的数据库。并且,Beego 的 ORM 并没有自己管理连接,而是直接依赖于驱动。
设置最大连接数一共有两种方式:
MaxOpenConnections()
选项:orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8", orm.MaxOpenConnections(100))
orm.SetMaxOpenConns("default", 30)
最大空闲连接数同样有两种方式
ORM 默认使用 time.Local
本地时区
如果要进行更改,只需要改变DefaultTimeLoc
的属性即可
// 设置为 UTC 时间
orm.DefaultTimeLoc = time.UTC
Beego 的 ORM 模块要求在使用之前要先注册好模型,并且 Beego 会执行一定的校验,用于辅助检查模型和模型之间的约束。并且模型定义也会影响自动建表功能自动建表
Beego 的模型定义,大部分都是依赖于 Go 标签特性,可以设置多个特性,用;
分隔。同一个特性的不同值使用,
来分隔。
例如:
orm:"null;rel(fk)"
注册模型有三个方法:
RegisterModel(models ...interface{})
RegisterModelWithPrefix(prefix string, models ...interface{})
:该方法会为表名加上前缀,例如RegisterModelWithPrefix("tab_", &User{})
,那么表名是tab_user
;RegisterModelWithSuffix(suffix string, models ...interface{})
:该方法会为表名加上后缀,例如RegisterModelWithSuffix("_tab", &User{})
,那么表名是user_tab
user := new(User)
// 表名为user
orm.RegisterModel(user)
// 表名为tab_user
orm.RegisterModelWithPrefix("tab_",user)
// 表名为user_tab
orm.RegisterModelWithSuffix("_tab",user)
默认的表名规则,使用驼峰转蛇形:
AuthUser -> auth_user
Auth_User -> auth__user
DB_AuthUser -> d_b__auth_user
除了开头的大写字母以外,遇到大写会增加 _
,原名称中的下划线保留。
也可以自定义表名,只需要实现接口TableNameI
:
type User struct {
Id int
Name string
}
func (u *User) TableName() string {
return "auth_user"
}
这时候注册:
user := new(User)
// 表名为auth_user
orm.RegisterModel(user)
同时,也可以在注册模型的时候为表名加上前缀或者后缀
创建一个简单的orm实例
var o orm.Ormer
o = orm.NewOrm() // 创建一个 Ormer
// NewOrm 的同时会执行 orm.BootStrap (整个 app 只执行一次),用以验证模型之间的定义并缓存。
大多数情况下,复用Orm
实例,因为本身Orm
实例被设计为无状态的,一个数据库对应一个Orm
实例。
但是在使用事务的时候,我们会返回TxOrm
的实例,它本身是有状态的,一个事务对应一个TxOrm
实例。在使用TxOrm
时候,任何衍生查询都是在该事务内。
Insert 和 InsertWithCtx
Insert(md interface{}) (int64, error)
InsertWithCtx(ctx context.Context, md interface{}) (int64, error)
使用:
// 创建用户
user := new(User)
user.Name = "lisi"
// 添加数据到表中
o.Insert(user)
这两个方法都只接收指针做为参数。
InsertOrUpdate 和 InsertOrUpdateWithCtx
InsertOrUpdate(md interface{}, colConflitAndArgs ...string) (int64, error)
InsertOrUpdateWithCtx(ctx context.Context, md interface{}, colConflitAndArgs ...string) (int64, error)
在 MySQL 中,执行 ON DUPLICATE KEY
。因此最后一个参数colConflictAndArgs
不需要传;
InsertMulti 和 InsertMultiWithCtx
用于执行批量插入:
InsertMulti(bulk int, mds interface{}) (int64, error)
InsertMultiWithCtx(ctx context.Context, bulk int, mds interface{}) (int64, error)
参数bulk
是每一次批量插入的时候插入的数量。例如bulk<=1
代表每一批插入一条数据,而如果bulk=3
代表每次插入三条数据。你需要仔细选择批次大小,它对插入性能有很大影响。大多数情况下,你可以把bulk
设置成数据量大小。
mds
必须是一个数组,或者是一个切片。
第一个返回值表示最终插入了多少数据。
Update 和 UpdateWithCtx
使用主键来更新数据。也就是如果你使用这个方法,Beego 会尝试读取里面的主键值,而后将主键作为更新的条件。
定义:
Update(md interface{}, cols ...string) (int64, error)
UpdateWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error)
如果你没有指定 cols
参数,那么所有的列都会被更新。
第一个返回值是受影响的行数。
使用:
结构体User增加一个属性age
type User struct {
ID int `orm:"column(id)"`
Name string `orm:"column(name)"`
Age int `orm:"column(age)"`
}
添加三条数据:
使用:
user := new(User)
user.ID = 1
user.Name = "zhangsan1"
// 添加数据到表中
i,_:=o.Update(user)
fmt.Println(i)
执行代码后,可以看到受影响的行数是1,数据库中第一条数据的name改为zhangsan1
,同时age变为0
当我们修改数据时没有指定对应属性的值时,系统会将数据库中对应列修改为属性类型的默认值,上面的age是int类型,默认值为0,所以变为0
如果只想修改name的值,需要使用cols
参数来指定
user := new(User)
user.ID = 1
user.Name = "zhangsan2"
// 添加数据到表中
i,_:=o.Update(user,"name")
fmt.Println(i)
只修改name的值,cols
参数为"name",不区分大小写
如果没有指定主键的值,则不进行修改
Delete 和 DeleteWithCtx
使用主键来删除数据,定义:
Delete(md interface{}, cols ...string) (int64, error)
DeleteWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error)
第一个返回值是受影响的行数。
使用:
user := new(User)
user.ID = 3
// 添加数据到表中
i,_:=o.Delete(user)
fmt.Println(i)
删除三号数据
注意:必须使用主键来进行匹配,不能使用其它属性的值
事务依赖于 Orm
实例。
ORM 操作事务,支持两种范式。一种通过闭包的方式,由 Beego 本身来管理事务的生命周期。
// Beego will manage the transaction's lifecycle
// if the @param task return error, the transaction will be rollback
// or the transaction will be committed
err := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
// data
user := new(User)
user.Name = "test_transaction"
// insert data
// Using txOrm to execute SQL
_, e := txOrm.Insert(user)
// if e != nil the transaction will be rollback
// or it will be committed
return e
})
在这种方式里面,第一个参数是task
,即该事务所有完成的动作。注意的是,如果它返回了 error,那么 Beego 会将整个事务回滚。
否则提交事务。
另外一个要注意的是,如果在task
执行过程中,发生了panic
,那么 Beego 会回滚事务。
推荐使用这种方式。
和事务相关的方法有:
// 需要自己管理事务生命周期
Begin() (TxOrmer, error)
BeginWithCtx(ctx context.Context) (TxOrmer, error)
BeginWithOpts(opts *sql.TxOptions) (TxOrmer, error)
BeginWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions) (TxOrmer, error)
// Beego 利用闭包管理生命周期
DoTx(task func(ctx context.Context, txOrm TxOrmer) error) error
DoTxWithCtx(ctx context.Context, task func(ctx context.Context, txOrm TxOrmer) error) error
DoTxWithOpts(opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error
DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error
这是一个用来处理日志的库,它的设计思路来自于 database/sql
,目前支持的引擎有 file、console、net、smtp、es、slack。
首先引入包:
import (
"github.com/beego/beego/v2/core/logs"
)
然后添加输出引擎(log 支持同时输出到多个引擎),这里我们以 console 为例,第一个参数是引擎名:
logs.SetLogger(logs.AdapterConsole)
添加输出引擎也支持第二个参数,用来表示配置信息,对于不同的引擎来说,其配置也是不同的。详细的配置请看下面介绍:
logs.SetLogger(logs.AdapterFile,`{"filename":"project.log","level":7,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10,"color":true}`)
然后我们就可以在我们的逻辑中开始任意的使用了:
package main
import (
"github.com/beego/beego/v2/core/logs"
)
func main() {
//an official log.Logger
l := logs.GetLogger()
l.Println("this is a message of http")
//an official log.Logger with prefix ORM
logs.GetLogger("ORM").Println("this is a message of orm")
logs.Debug("my book is bought in the year of ", 2016)
logs.Info("this %s cat is %v years old", "yellow", 3)
logs.Warn("json is a type of kv like", map[string]int{"key": 2016})
logs.Error(1024, "is a very", "good game")
logs.Critical("oh,crash")
}
一般推荐使用通用方式进行日志,但依然支持单独声明来使用独立的日志
package main
import (
"github.com/beego/beego/v2/core/logs"
)
func main() {
log := logs.NewLogger()
log.SetLogger(logs.AdapterConsole)
log.Debug("this is a debug message")
}
为了提升性能, 可以设置异步输出:
logs.Async()
异步输出允许设置缓冲 chan 的大小
logs.Async(1e3)
在一些情况下,我们可能需要自己定义自己的日志格式规范。这种时候,可以考虑通过扩展LogFormatter
。
type LogFormatter interface {
Format(lm *LogMsg) string
}
LogMsg
包含了一条日志的所有部分。需要注意的是,如果你希望输出文件名和行号,那么应该参考输出文件名和行号,设置对应的参数。
该实现的设计思路,是希望能够使用类似于占位符的东西来定义一条日志应该如何输出。
例子:
package main
import (
"github.com/beego/beego/v2/core/logs"
)
func main() {
f := &logs.PatternLogFormatter{
Pattern: "%F:%n|%w%t>> %m",
WhenFormat: "2006-01-02",
}
logs.RegisterFormatter("pattern", f)
_ = logs.SetGlobalFormatter("pattern")
logs.Info("hello, world")
}
我们先初始化了一个PatternLogFormatter
实例,而后注册为pattern
。
再然后我们使用logs.SetGlobalFormatter("pattern")
设置全局所有的引擎都使用这个格式。
最终我们输出日志/beego-example/logger/formatter/pattern/main.go:31|2020-10-29[I]>> hello, world
如果我们只希望在某个特定的引擎上使用这个格式,我们可以通过初始化引擎的时候,设置:
_ = logs.SetLogger("console",`{"formatter": "pattern"}`)
PatternLogFormatter
支持的占位符及其含义:
[I]
代表 INFOconn: 网络输出,设置的例子如下所示:
logs.SetLogger(logs.AdapterConn, `{"net":"tcp","addr":":7020"}`)
主要的参数说明如下:
smtp: 邮件发送,设置的例子如下所示:
logs.SetLogger(logs.AdapterMail, `{"username":"[email protected]","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["[email protected]"]}`)
主要的参数说明如下:
username
: smtp
验证的用户名password
: smtp
验证密码host
: 发送的邮箱地址sendTos
: 邮件需要发送的人,支持多个subject
: 发送邮件的标题,默认是 Diagnostic message from server
level
: 日志发送的级别,默认是 Trace 级别ElasticSearch:输出到 ElasticSearch:
logs.SetLogger(logs.AdapterEs, `{"dsn":"http://localhost:9200/","level":1}`)
简聊: 输出到简聊:
logs.SetLogger(logs.AdapterJianLiao, `{"authorname":"xxx","title":"beego", "webhookurl":"https://jianliao.com/xxx", "redirecturl":"https://jianliao.com/xxx","imageurl":"https://jianliao.com/xxx","level":1}`)
slack: 输出到 slack
logs.SetLogger(logs.AdapterSlack, `{"webhookurl":"https://slack.com/xxx","level":1}`)