一,beego簡介
1,beego是一個go語言框架,可以用來快速開發(fā)API、Web、后端服務(wù)等各種應(yīng)用。
2,beego的安裝,包地址即源碼github地址,go get命令借助代碼管理工具如git通過遠(yuǎn)程拉取或更新代碼包及其依賴包,并自動完成編譯和安裝。
go get github.com/astaxie/beego
go get github.com/beego/bee
3,文件結(jié)構(gòu)及邏輯
beego在執(zhí)行邏輯上是一個典型的 MVC 架構(gòu):

二,beego流程及使用
通過使用bee工具的命令可以分別生成web和api兩種項(xiàng)目結(jié)構(gòu)
bee new myproject
bee api myapi
入口文件
- 文件名可以隨意命名
- 初始化路由配置,監(jiān)聽http請求
路由配置
- 固定式路由 參數(shù):路徑,controller地址,函數(shù)方法名
package routers
import (
"myapi/controllers"
"github.com/astaxie/beego"
)
func init() {
beego.Router("/", &controllers.MainController{})
beego.Router("/v1/area/getArea", &controllers.AreaController{}, "post:GetArea")
}
- 正則路由 正則匹配路徑及參數(shù)
beego.Router(“/api/:id([0-9]+)“, &controllers.RController{})
beego.Router(“/user/:username([\\w]+)“, &controllers.RController{})
- 自動匹配路由
beego.AutoRouter(&controllers.ObjectController{})
/object/login 調(diào)用 ObjectController 中的 Login 方法
/object/logout 調(diào)用 ObjectController 中的 Logout 方法
- 注解式路由 路由里使用namespace引入文件,函數(shù)方法的上方加上router注釋
beego.Include(&CMSController{})
// CMS API
type CMSController struct {
beego.Controller
}
// @router /staticblock [post]
func (this *CMSController) StaticBlock() {
}
控制器
- 通過type struct結(jié)構(gòu)體引入實(shí)現(xiàn)了類似繼承的功能
type xxxController struct {
beego.Controller
}
- beego.Controller 實(shí)現(xiàn)了接口 beego.ControllerInterface 定義了如下函數(shù):
Init(ct *context.Context, childName string, app interface{})
這個函數(shù)主要初始化了 Context、相應(yīng)的 Controller 名稱,模板名,初始化模板參數(shù)的容器 Data
Prepare() 這個函數(shù)主要是為了用戶擴(kuò)展用的,用戶可以重寫這個函數(shù)實(shí)現(xiàn)類似用戶驗(yàn)證之類。
Get() 如果用戶請求的 HTTP Method 是 GET, 處理 Get 請求。
Post() 如果用戶請求的 HTTP Method 是 POST, 處理 Post 請求。
Delete() 如果用戶請求的 HTTP Method 是 DELETE, 處理 Delete 請求。
Put() 如果用戶請求的 HTTP Method 是 PUT,處理 Put 請求.
Head() 如果用戶請求的 HTTP Method 是 HEAD, 處理 Head 請求。
Patch() 如果用戶請求的 HTTP Method 是 PATCH, 處理 Patch 請求.
Options() 如果用戶請求的HTTP Method是OPTIONS, 處理 Options 請求。
Finish() 這個函數(shù)是在執(zhí)行完相應(yīng)的 HTTP Method 方法之后執(zhí)行的,默認(rèn)是空, 執(zhí)行例如數(shù)據(jù)庫關(guān)閉,清理數(shù)據(jù)之類的工作。
Render() error 這個函數(shù)主要用來實(shí)現(xiàn)渲染模板,如果 beego.AutoRender 為 true 的情況下才會執(zhí)行。
模型orm
- 默認(rèn)支持MySQL、PostgreSQL和Sqlite3 三種數(shù)據(jù)庫
- 數(shù)據(jù)庫連接及增刪改查示例
func init() {
// set default database
orm.RegisterDataBase("default", "mysql", "username:password@tcp(127.0.0.1:3306)/db_name?charset=utf8", 30)
// register model
orm.RegisterModel(new(User))
// create table
orm.RunSyncdb("default", false, true)
}
func main() {
o := orm.NewOrm()
user := User{Name: "slene"}
// insert
id, err := o.Insert(&user)
fmt.Printf("ID: %d, ERR: %v\n", id, err)
// update
user.Name = "astaxie"
num, err := o.Update(&user)
fmt.Printf("NUM: %d, ERR: %v\n", num, err)
// read one
u := User{Id: user.Id}
err = o.Read(&u)
fmt.Printf("ERR: %v\n", err)
// delete
num, err = o.Delete(&u)
fmt.Printf("NUM: %d, ERR: %v\n", num, err)
}
- 關(guān)聯(lián)查詢,關(guān)聯(lián)方式大體有三種,均需在tag注解中說明:
1, 一對一orm:"rel(one)"反向:orm:"reverse(one)"
2, 一對多orm:"rel(fk)"反向:orm:"reverse(many)"
4, 多對多orm:"rel(m2m)"反向:orm:"reverse(many)"
type Post struct {
Id int `orm:"auto"`
Title string `orm:"size(100)"`
User *User `orm:"rel(fk)"`
}
var posts []*Post
qs := o.QueryTable("post")
num, err := qs.Filter("User__Name", "slene").All(&posts)
- 原生sql查詢
var maps []orm.Params
num, err := o.Raw("SELECT * FROM user").Values(&maps)
for _,term := range maps{
fmt.Println(term["id"],":",term["name"])
}
- 事務(wù)處理
o.Begin()
...
user := User{Name: "slene"}
id, err := o.Insert(&user)
if err == nil {
o.Commit()
} else {
o.Rollback()
}
- 高級查詢操作符
exact / iexact 等于
contains / icontains 包含
gt / gte 大于 / 大于等于
lt / lte 小于 / 小于等于
startswith / istartswith 以…起始
endswith / iendswith 以…結(jié)束
in
isnull
后面以 i 開頭的表示:大小寫不敏感
- 高級查詢操作符示例:
qs.Filter("name__exact", "slene") // WHERE name = 'slene'
qs.Filter("name__contains", "slene") // WHERE name LIKE BINARY '%slene%'
ids:=[]int{17,18,19,20}; qs.Filter("age__in", ids) // WHERE age IN (17, 18, 19, 20)
qs.Filter("profile__age__gt", 17) // WHERE profile.age > 17
qs.Filter("name__startswith", "slene") // WHERE name LIKE BINARY 'slene%'
qs.Filter("profile__id__isnull", true) // WHERE profile_id IS NULL
//select示例
orm1.QueryTable(TableName("user")).Filter("username__contains", m.Username).Limit(10, 0).OrderBy("-id").All(&users,"id","username");
- 構(gòu)造查詢接口
// User 包裝了下面的查詢結(jié)果
type User struct {
Id int
Username string
Age int
}
var users []User
// 獲取 QueryBuilder 對象. 需要指定數(shù)據(jù)庫驅(qū)動參數(shù)。
// 第二個返回值是錯誤對象,在這里略過
qb, _ := orm.NewQueryBuilder("mysql")
// 構(gòu)建查詢對象
qb.Select("a.id","a.username","b.birthday").
From(TableName("user") + " as a").
InnerJoin(TableName("user_profile") + " as b").On("a.id = b.user_id").
Where("a.username like '"+ m.Username +"%'").
OrderBy("a.id").Desc().
Limit(10).Offset(0)
// 導(dǎo)出 SQL 語句
sql := qb.String()
// 執(zhí)行 SQL 語句
o := orm.NewOrm()
o.Raw(sql, 20).QueryRows(&users)
beego 模板語法
- go 統(tǒng)一使用了
{{和}}作為左右標(biāo)簽,沒有其他的標(biāo)簽符號。如果您想要修改為其它符號,可以參考 模板標(biāo)簽。 - 使用
.來訪問當(dāng)前位置的上下文 - 數(shù)據(jù)輸出
func (this *MainController) Get() {
this.Data["Website"] = "beego.me"
this.Data["Email"] = "astaxie@gmail.com"
this.TplName = "index.tpl"
}
- if … else … end
{{if .IsHome}}
{{else}}
{{if .IsAbout}}{{end}}
{{end}}
- range … end
{{range .Pages}}
{{.Num}} of {{$.Total}}
{{end}}
- beego 中支持直接載入文件模板
{{template "path/to/head.html" .}}
beego 單元測試
- 只有唯一的參數(shù),必須是 t *testing.T 類型
- 必須以單詞 Test 開頭,再組合上首字母大寫的單詞或詞組(一般是被測試的方法名稱,如 TestValidateClient)
- 調(diào)用 t.Error 或者 t.Fail 方法指明測試失?。ㄟ@里我使用了 t.Errorf 來提供更多的細(xì)節(jié))
t.Log 可以用來提供一些失敗信息以外的調(diào)試信息 - 測試代碼文件名必須是 _test 結(jié)尾的形式 something_test.go ,例如:addtion_test.go
1,功能測試
go test user_test.go -v -run="TestGetUser"
// TestGetUser is a sample to run an endpoint test
func TestGetUser(t *testing.T) {
param := map[string]string{
"username": "a",
}
marshaled, _ := json.Marshal(param)
r, err := http.NewRequest("Post", "/v1/user/GetUser", bytes.NewBuffer(marshaled))
if err != nil {
t.Fatalf("should get user success, but fails to send request, error:%s\n", err)
}
w := httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
beego.Trace("testing", "TestGetUser", "Code[%d]\n%s", w.Code, w.Body.String())
}
2,性能測試
go test user_test.go -v -bench="BenchmarkGetUser"
// BenchmarkGetUser
func BenchmarkGetUser(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
param := map[string]string{
"username": "a",
}
marshaled, _ := json.Marshal(param)
r, err := http.NewRequest("Post", "/v1/user/GetUser", bytes.NewBuffer(marshaled))
if err != nil {
b.Fatalf("should get user success, but fails to send request, error:%s\n", err)
}
w := httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
beego.Trace("testing", "TestGetUser", "Code[%d]\n%s", w.Code, w.Body.String())
}
}
其他
1,import 導(dǎo)入包時,初始化init函數(shù)里用到,但是主體各函數(shù)里沒用到,引入包名左側(cè)要有個下劃線別名,否則會報錯,例如數(shù)據(jù)庫連接初始化
2, Go語言要求public的變量必須以大寫字母開頭,private變量則以小寫字母開頭, 函數(shù)名命名也遵循這個規(guī)則,否則小寫開頭的變量和函數(shù)名不能被外部訪問
3,JSON輸出的時候必須注意,只有導(dǎo)出的字段(首字母是大寫)才會被輸出,如果修改字段名,那么就會發(fā)現(xiàn)什么都不會輸出,所以必須通過struct tag(結(jié)構(gòu)體注解)定義來實(shí)現(xiàn)。針對JSON的輸出,我們在定義struct tag的時候需要注意的幾點(diǎn)是:
- 字段的tag是"-",那么這個字段不會輸出到JSON
- tag中帶有自定義名稱,那么這個自定義名稱會出現(xiàn)在JSON的字段名中
- tag中如果帶有"omitempty"選項(xiàng),那么如果該字段值為空,就不會輸出到JSON串中
- 如果字段類型是bool, string, int, int64等,而tag中帶有",string"選項(xiàng),那么這個字段在輸出到JSON的時候會把該字段對應(yīng)的值轉(zhuǎn)換成JSON字符串
4,Beego框架orm在執(zhí)行o.Insert(*p)插入數(shù)據(jù)時候需要傳入指針變量作為參數(shù),原因是因?yàn)樵诓迦氤晒?,會返回id給user
5,當(dāng)字段是指針類型時,如果沒有用orm:"-"進(jìn)行orm忽略,必須要添加標(biāo)簽來進(jìn)行表關(guān)系設(shè)置。
6,多個接口返回字段不一樣時,如果公用結(jié)構(gòu)體指針查詢的值,對輸出的字段不好增減控制,建議接口方法內(nèi)每次對接口輸出的字段自定義結(jié)構(gòu)體