1. 程式人生 > >怎樣用beego開發服務端應用?(轉)

怎樣用beego開發服務端應用?(轉)

說明

beego是國內團隊開源的golang開發框架,是一個關注度和使用量都比價高的專案。

Quick Start

安裝

首先需要安裝beego和bee工具:

$ go get -u github.com/astaxie/beego
$ go get -u github.com/beego/bee

為了能夠直接使用bee命令,需要將$GOPATH/bin新增到$PATH變數中。

建立應用

建立一個名為hello的應用,可以選擇web應用,或者api應用:

$ bee new hello     //建立一個web應用
$ bee api hello     //建立一個api應用

執行結束後,會在當前目錄下建立名為hello

的目錄:

.
|____hello
| |____conf
| | |____app.conf
| |____controllers
| | |____default.go
| |____main.go
| |____models
| |____routers
| | |____router.go
| |____static
| | |____css
| | |____img
| | |____js
| | | |____reload.min.js
| |____tests
| | |____default_test.go
| |____views
| | |____index.tpl

編譯執行

進入hello目錄中,執行bee run,就會完成編譯、執行:

$ cd hello/
$ bee run
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.9.1
2017/10/23 14:33:05 INFO     ▶ 0001 Using 'hello' as 'appname'
2017/10/23 14:33:05 INFO     ▶ 0002 Initializing watcher...
2017/10/23 14:33:06 SUCCESS  ▶ 0003 Built Successfully!
2017/10/23 14:33:06 INFO     ▶ 0004 Restarting 'hello'...
2017/10/23 14:33:06 SUCCESS  ▶ 0005 './hello' is running...
2017/10/23 14:33:06 [I] [asm_amd64.s:2197] http server Running on http://:8080

打包釋出

專案打包釋出:

$ bee pack

程式碼生成

生成models:

bee generate model user -fields="name:string,age:int"

生成controller:

bee generate controller user

生成view:

bee generate view user

生成文件:

bee generate docs

開發文件

beego開發文件中對beego做了很詳細的說明。

beego執行邏輯

目錄結構說明

.
|____hello
| |____conf           <- 配置檔案
| | |____app.conf
| |____controllers    <- 控制器,即http請求的handler
| | |____default.go
| |____main.go        <- main函式
| |____models
| |____routers        <- 路由,將url關聯到controllers
| | |____router.go
| |____static         <- 靜態檔案
| | |____css
| | |____img
| | |____js
| | | |____reload.min.js
| |____tests          <- 測試
| | |____default_test.go
| |____views          <- 頁面模版,controller中可以直接渲染對應的tpl檔案
| | |____index.tpl

使用配置檔案

beego引數配置中講解如何使用配置檔案、如何配置引數。

beego預設解析應用目錄下的conf/app.conf檔案,配置項可以通過beego.AppConfig.*讀取:

beego.AppConfig.String("mysqluser")

beego.Appconfig包含多個方法:

Set(key, val string) error
String(key string) string
Strings(key string) []string
Int(key string) (int, error)
Int64(key string) (int64, error)
Bool(key string) (bool, error)
Float(key string) (float64, error)
DefaultString(key string, defaultVal string) string
DefaultStrings(key string, defaultVal []string)
DefaultInt(key string, defaultVal int) int
DefaultInt64(key string, defaultVal int64) int64
DefaultBool(key string, defaultVal bool) bool
DefaultFloat(key string, defaultVal float64) float64
DIY(key string) (interface{}, error)
GetSection(section string) (map[string]string, error)
SaveConfigFile(filename string) error

配置檔案中可以配置section,通過runmode指定要使用的section,例如:

appname = beepkg
httpaddr = "127.0.0.1"
httpport = 9090
runmode ="dev"
autorender = false
recoverpanic = false
viewspath = "myview"

[dev]
httpport = 8080
[prod]
httpport = 8088
[test]
httpport = 8888

對於section的配置項,通過下面的方式讀取,模式::配置引數名:

beego.AppConfig.String(“dev::mysqluser”)

配置檔案中可以使用環境變數:

runmode  = "${ProRunMode||dev}"
httpport = "${ProPort||9090}"

載入特定的配置檔案,可以載入多個配置檔案,key不能衝突:

beego.LoadAppConfig("ini", "conf/app2.conf")
beego.LoadAppConfig("ini", "conf/app3.conf")

beego預設引數

beego的預設引數全部儲存在beego.BConfig中。,可以訪問、修改所有的配置資訊。

在配置檔案中設定的同名的key(不區分大小寫)的值會覆蓋預設值,例如:

appname = hello
httpport = 8080
runmode = dev

[dev]
AutoRender=false    #會覆蓋beego.BConfig.WebConfig.AutoRender的預設值

[test]

[prod]

App配置:

beego.BConfig.AppName = "beego"
beego.BConfig.RunMode = "dev"
beego.BConfig.RouterCaseSensitive = true
beego.BConfig.ServerName = "beego"
beego.BConfig.RecoverPanic = true
beego.BConfig.EnableGzip = false
beego.BConfig.MaxMemory = 1 << 26
beego.BConfig.EnableErrorsShow = true
beego.BConfig.EnableErrorsRender = true

Web配置:

beego.BConfig.WebConfig.AutoRender = true
beego.BConfig.WebConfig.EnableDocs = true
beego.BConfig.WebConfig.FlashName = "BEEGO_FLASH"
beego.BConfig.WebConfig.FlashSeperator = "BEEGOFLASH"
beego.BConfig.WebConfig.DirectoryIndex = false
beego.BConfig.StaticDir = static
beego.BConfig.WebConfig.StaticExtensionsToGzip = []string{".css", ".js"}
beego.BConfig.WebConfig.TemplateLeft="
beego.BConfig.WebConfig.TemplateRight="
beego.BConfig.WebConfig.ViewsPath="views"
beego.BConfig.WebConfig.EnableXSRF = false
beego.BConfig.WebConfig.XSRFKEY = "beegoxsrf"
beego.BConfig.WebConfig.XSRFExpire = 0

監聽配置:

beego.BConfig.Listen.Graceful=false
beego.BConfig.Listen.ServerTimeOut=0
beego.BConfig.Listen.ListenTCP4 = "tcp4"
beego.BConfig.Listen.EnableHTTP = true
beego.BConfig.Listen.HTTPAddr = ""
beego.BConfig.Listen.HTTPPort = 8080
beego.BConfig.Listen.EnableHTTPS = false
beego.BConfig.Listen.HTTPSAddr = ""
beego.BConfig.Listen.HTTPSPort = 10443
beego.BConfig.Listen.HTTPSCertFile = "conf/ssl.crt"
beego.BConfig.Listen.HTTPSKeyFile = "conf/ssl.key"
beego.BConfig.Listen.EnableAdmin = false
beego.BConfig.Listen.AdminAddr = "localhost"
beego.BConfig.Listen.AdminPort = 8088
beego.BConfig.Listen.EnableFcgi = false
beego.BConfig.Listen.EnableStdIo = false

Session配置:

beego.BConfig.WebConfig.Session.SessionOn = false
beego.BConfig.WebConfig.Session.SessionProvider = ""
beego.BConfig.WebConfig.Session.SessionName = "beegosessionID"
beego.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600
beego.BConfig.WebConfig.SessionProviderConfig
beego.BConfig.WebConfig.Session.SessionCookieLifeTime = 3600
beego.BConfig.WebConfig.Session.SessionAutoSetCookie = true
beego.BConfig.WebConfig.Session.SessionDomain = ""

Log配置:

beego.BConfig.Log.AccessLogs = false
beego.BConfig.Log.FileLineNum = true
beego.BConfig.Log.Outputs = map[string]string{"console": ""}

日誌處理

學習一個框架,應當首先學習它的日誌,瞭解它是如何管理日誌的,合理使用日誌,可以節省非常多的時間。

一般可以直接使用下面的方式,輸出不同級別的日誌:

beego.Emergency("this is emergency")
beego.Alert("this is alert")
beego.Critical("this is critical")
beego.Error("this is error")
beego.Warning("this is warning")
beego.Notice("this is notice")
beego.Informational("this is informational")
beego.Debug("this is debug")

可以設定為輸出到檔案中:

beego.SetLogger("file", `{"filename":"logs/test.log"}`)

如果不想輸出到console:

beego.BeeLogger.DelLogger("console")

設定日誌顯示的級別:

beego.SetLevel(beego.LevelInformational)

可以選擇下面的級別:
LevelEmergency
LevelAlert
LevelCritical
LevelError
LevelWarning
LevelNotice
LevelInformational
LevelDebug

輸出檔名和行號:

beego.SetLogFuncCall(true)

路由設定

beego支援三種路由: 基礎路由、正則路由、自動路由。

路由的表述方式

支援用正則的方式書寫路由,參考了sinatra的路由實現。

 路由規則                           可以匹配
/api/?:id                ---->  /api/, /api/123 id=123
/api/:id                 ---->  /api/123  id=123
/api/:id([0-9]+)         ---->  /api/123  id=123
/user/:username([\w]+)   ---->  /user/abc username=abc
/download/*.*            ---->  /download/file/api.xml  path=file/api ext=xml
/download/ceshi/*        ---->  /download/cechis/file/api.json  splat=file/api.json
/:id:int                 ---->  等同於/:id([0-9]+)
/:hi:string              ---->  等同於/:hi([\w]+)
/cms_:id([0-9]+).html    ---->  /cms_123.html  id=123

可以通過*context.Context的Input.Param()方法讀取路由中的變數:

ctx.Input.Param(":id")

在Controller中,通過下面的方式獲取路由中的變數,this是controller:

this.Ctx.Input.Param(":id")
this.Ctx.Input.Param(":username")
this.Ctx.Input.Param(":splat")
this.Ctx.Input.Param(":path")
this.Ctx.Input.Param(":ext")

直接設定路由

routers/router.go中設定,可以使用下面的基礎函式直接設定路由:

beego.Get(router, beego.FilterFunc)
beego.Post(router, beego.FilterFunc)
beego.Put(router, beego.FilterFunc)
beego.Head(router, beego.FilterFunc)
beego.Options(router, beego.FilterFunc)
beego.Delete(router, beego.FilterFunc)
beego.Any(router, beego.FilterFunc)

例如:

//響應post /alice
beego.Post("/alice",func(ctx *context.Context){
	 ctx.Output.Body([]byte("bob"))
})

//響應到/foo的所有http請求
beego.Any("/foo",func(ctx *context.Context){
	 ctx.Output.Body([]byte("bar"))
})

以註冊handler的方式設定路由

也可以使用beego.Handler(router, http.Handler)設定路由的handler:

beego.Handler("/rpc", s)

beego.Handler預設是完全匹配,不是字首匹配。可以自定義http請求方法和處理函式的對映關係:

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")

自定義對映關係的格式為”請求方法:函式名”,請求方法有下面幾種:

*: 包含以下所有的函式,優先順序低於下面的方法
get: GET 請求
post: POST 請求
put: PUT 請求
delete: DELETE 請求
patch: PATCH 請求
options: OPTIONS 請求
head: HEAD 請求

自動註冊路由

另外還有beego.AutoRouter($controllers.ObjectController{}),會自動通過反射為Object中的方法生成路由。

通過註解註冊路由

在controller的方法上面加上router註釋,router.go中通過beego.Include(&Controller)引入controller的時候會自動註冊路由。

例如:

// CMS API
type CMSController struct {
	beego.Controller
}

func (c *CMSController) URLMapping() {
	c.Mapping("StaticBlock", c.StaticBlock)
	c.Mapping("AllBlock", c.AllBlock)
}

// @router /staticblock/:key [get]
func (this *CMSController) StaticBlock() {

}

// @router /all/:key [get]
func (this *CMSController) AllBlock() {

}

然後在router.go中:

beego.Include(&CMSController{})

beego會自動進行原始碼分析,如果是dev模式,會在routers/commentXXX.go檔案。

使用namespace管理路由

namespace支援前套,並且可以對包含其中對路由進行前置過濾、條件判斷。

namespace介面如下:

NewNamespace(prefix string, funcs …interface{})     初始化 namespace 物件
NSCond(cond namespaceCond)      支援滿足條件才namespace
NSBefore(filiterList …FilterFunc)
NSAfter(filiterList …FilterFunc)
NSInclude(cList …ControllerInterface)
NSRouter(rootpath string, c ControllerInterface, mappingMethods …string)
NSGet(rootpath string, f FilterFunc)
NSPost(rootpath string, f FilterFunc)
NSDelete(rootpath string, f FilterFunc)
NSPut(rootpath string, f FilterFunc)
NSHead(rootpath string, f FilterFunc)
NSOptions(rootpath string, f FilterFunc)
NSPatch(rootpath string, f FilterFunc)
NSAny(rootpath string, f FilterFunc)
NSHandler(rootpath string, h http.Handler)
NSAutoRouter(c ControllerInterface)
NSAutoPrefix(prefix string, c ControllerInterface)

示例:

//初始化 namespace
ns :=
beego.NewNamespace("/v1",
    beego.NSCond(func(ctx *context.Context) bool {
        if ctx.Input.Domain() == "api.beego.me" {
            return true
        }
        return false
    }),
    beego.NSBefore(auth),
    beego.NSGet("/notallowed", func(ctx *context.Context) {
        ctx.Output.Body([]byte("notAllowed"))
    }),
    beego.NSRouter("/version", &AdminController{}, "get:ShowAPIVersion"),
    beego.NSRouter("/changepassword", &UserController{}),
    beego.NSNamespace("/shop",
        beego.NSBefore(sentry),
        beego.NSGet("/:id", func(ctx *context.Context) {
            ctx.Output.Body([]byte("notAllowed"))
        }),
    ),
    beego.NSNamespace("/cms",
        beego.NSInclude(
            &controllers.MainController{},
            &controllers.CMSController{},
            &controllers.BlockController{},
        ),
    ),
)
//註冊 namespace
beego.AddNamespace(ns)

註冊了以下的路由:

GET /v1/notallowed
GET /v1/version
GET /v1/changepassword
POST /v1/changepassword
GET /v1/shop/123
GET /v1/cms/ 對應 MainController、CMSController、BlockController 中得註解路由

需要特別注意的NSAfter()

NSAfter()註冊的filter函式會在請求處理結束的時候被呼叫,但是要注意在bee 1.9.0中:

beego.NSAfter does not work after controller.ServeJSON

相關的issue:

自動生成

從資料庫一鍵生成程式碼

bee generate appcode [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-level=3]
    generate appcode based on an existing database
    -tables: a list of table names separated by ',', default is empty, indicating all tables
    -driver: [mysql | postgres | sqlite], the default is mysql
    -conn:   the connection string used by the driver.
             default for mysql:    root:@tcp(127.0.0.1:3306)/test
             default for postgres: postgres://postgres:[email protected]:5432/postgres
    -level:  [1 | 2 | 3], 1 = models; 2 = models,controllers; 3 = models,controllers,router

例如為資料庫中所有的表生成models、controllers、router:

bee generate appcode -driver=mysql -conn="root:@tcp(127.0.0.1:3306)/kube-loadbalance" -level=3

$ ls models/
backend.go      listener.go     object.go       resource.go     user.go
cluster.go      loadbalancer.go pool.go         template.go

$ ls controllers/
backend.go      listener.go     object.go       resource.go     user.go
cluster.go      loadbalancer.go pool.go         template.go

routers/routers.go中也自動生成了路由:

func init() {
    ns := beego.NewNamespace("/v1",

        beego.NSNamespace("/backend",
            beego.NSInclude(
                &controllers.BackendController{},
            ),
        ),

        beego.NSNamespace("/cluster",
            beego.NSInclude(
                &controllers.ClusterController{},
            ),
        ),
...

生成controller

bee generate controller user

生成models

bee generate model user -fields="name:string,age:int"

生成view

bee generate view user

生成文件

自動建立swagge檔案:

$ bee generate docs

啟動後訪問/swagger檢視api:

func main() {
	if beego.BConfig.RunMode == "dev" {
		beego.BConfig.WebConfig.DirectoryIndex = true
		beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
	}
	beego.Run()
}

注意,如果是第一次執行,帶上-downdoc=true

bee run -downdoc=true

API自動文件語法

API自動化文件在routers/router.go和每個controller中設定。

全域性設定必須在router.go中,且在檔案的最頂部:

// @APIVersion 1.0.0
// @Title mobile API
// @Description mobile has every tool to get any job done, so codename for the new mobile APIs.
// @Contact [email protected]
// @TermsOfServiceUrl
// @License
// @LicenseUrl
package routers

controler必須是以namespace+include的方式註冊到路由中,否則controller中的文件註釋不生效。

// @Title Get Product list
// @Description Get Product list by some info
// @Success 200 {object} models.ZDTProduct.ProductList
// @Param   category_id     query   int false       "category id"
// @Param   brand_id    query   int false       "brand id"
// @Param   query   query   string  false       "query of search"
// @Param   segment query   string  false       "segment"
// @Param   sort    query   string  false       "sort option"
// @Param   dir     query   string  false       "direction asc or desc"
// @Param   offset  query   int     false       "offset"
// @Param   limit   query   int     false       "count limit"
// @Param   price           query   float       false       "price"
// @Param   special_price   query   bool        false       "whether this is special price"
// @Param   size            query   string      false       "size filter"
// @Param   color           query   string      false       "color filter"
// @Param   format          query   bool        false       "choose return format"
// @Failure 400 no enough input
// @Failure 500 get products common error
// @router /products [get]
func (c *CMSController) Product() {
...
}

Param後面的引數含義分別是:

引數名       輸入方式               引數型別        是否必須   註釋
        formData: post傳送的資料    string           true
        query   : url引數           int              false
        path    : 路徑引數          float
        body    : raw資料請求       PACKAGE.STRUCT
        header  : Header中的引數

使用bee命令啟動應用:

bee run --gendoc=true -downdoc=true

現在就可以訪問http://127.0.0.1:8080/swagger/檢視api文件。

連線資料庫

beego仿照Digango ORM和SQLAlchemy實現beego ORM,當前支援三個驅動:

MySQL:     github.com/go-sql-driver/mysql
PostgreSQL:github.com/lib/pq
Sqlite3:   github.com/mattn/go-sqlite3

beego生成的model檔案中,會自動將model註冊到orm,例如:

bee generate model user -fields="name:string,age:int"

生成的程式碼models/user.go中會在init()中註冊:

func init() {
	orm.RegisterModel(new(User))
}

因此只需要手工書寫orm初始化的程式碼,譬如在main.go中:

func init() {
	orm.RegisterDataBase("default", "mysql", "root:@tcp(127.0.0.1:3306)/mysql?charset=utf8", 30)
}

資料庫遷移(migration)

資料庫遷移功能可以資料庫進行升級、回滾操作。

生成遷移檔案,user是表名,fields是表結構:

bee generate migration user -driver=mysql -fields="name:string,age:int"

執行後,生成了檔案:

|____database
| |____migrations
| | |____20171024_154037_user.go

執行下面的命令:

bee migrate -driver=mysql -conn="root:@tcp(127.0.0.1:3306)/study-beego"

study-beego中的表將會被建立或者更新,並在名為migrations的表中記錄更新。

bee migrate

migrate的子命令refreshrollback執行失敗,原因不明。

beego.Controller處理http請求

注意,在1.9.0中,需要在配置中設定copyrequestbody=true以後,c.Ctx.Input.RequestBody中才有資料。

func (c *UserController) Post() {
	var v models.User
	json.Unmarshal(c.Ctx.Input.RequestBody, &v)
	fmt.Println(v)
	if _, err := models.AddUser(&v); err == nil {
		c.Ctx.Output.SetStatus(201)
		c.Data["json"] = v
	} else {
		c.Data["json"] = err.Error()
	}
	c.ServeJSON()
}

參考