controller 邏輯
controller就是處理具體的邏輯的郁妈,router將請(qǐng)求分發(fā)到指定的controlller,controller處理請(qǐng)求绍申,然后返回噩咪。
首先我們還是從源碼分析入手:
package controllers
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Data["Website"] = "beego.me"
this.Data["Email"] = "astaxie@gmail.com"
this.TplName = "index.tpl"
}
上面的代碼顯示首先我們聲明了一個(gè)控制器 MainController
顾彰,這個(gè)控制器里面內(nèi)嵌了 beego.Controller
,這就是 Go 的嵌入方式胃碾,也就是 MainController
自動(dòng)擁有了所有 beego.Controller
的方法涨享。
而 beego.Controller
擁有很多方法,其中包括 Init
仆百、Prepare
厕隧、Post
、Get
俄周、Delete
吁讨、Head
等方法。我們可以通過重寫的方式來實(shí)現(xiàn)這些方法栈源,而我們上面的代碼就是重寫了 Get
方法挡爵。
beego 是一個(gè) RESTful 的框架,請(qǐng)求默認(rèn)是執(zhí)行對(duì)應(yīng) req.Method
的方法甚垦。例如瀏覽器的是 GET
請(qǐng)求茶鹃,那么默認(rèn)就會(huì)執(zhí)行 MainController
下的 Get
方法。這樣上面的 Get 方法就會(huì)被執(zhí)行到艰亮,就進(jìn)入了具體的邏輯處理闭翩。
里面的代碼是需要執(zhí)行的邏輯,這里只是簡(jiǎn)單的輸出數(shù)據(jù)迄埃,我們可以通過各種方式獲取數(shù)據(jù)疗韵,然后賦值到 this.Data
中,這是一個(gè)用來存儲(chǔ)輸出數(shù)據(jù)的 map侄非,可以賦值任意類型的值蕉汪,這里我們只是簡(jiǎn)單舉例輸出兩個(gè)字符串。
最后一個(gè)就是需要去渲染的模板逞怨,this.TplName
就是需要渲染的模板者疤,這里指定了 index.tpl
,如果用戶不設(shè)置該參數(shù)叠赦,那么默認(rèn)會(huì)去到模板目錄的 Controller/<方法名>.tpl
查找驹马,例如上面的方法會(huì)去 maincontroller/get.tpl
(文件、文件夾必須小寫)除秀。
用戶設(shè)置了模板之后系統(tǒng)會(huì)自動(dòng)的調(diào)用 Render
函數(shù)(這個(gè)函數(shù)是在 beego.Controller 中實(shí)現(xiàn)的)糯累,所以無需用戶自己來調(diào)用渲染。
當(dāng)然也可以不使用模版册踩,直接用 this.Ctx.WriteString
輸出字符串泳姐,如:
func (this *MainController) Get() {
this.Ctx.WriteString("hello")
}
下面詳細(xì)介紹controller:
控制器介紹
基于 beego 的 Controller 設(shè)計(jì),只需要匿名組合 beego.Controller
就可以了棍好,如下所示:
type xxxController struct {
beego.Controller
}
我們來看一下beego.Controller的源碼
type Controller struct {
// context data
Ctx *context.Context
Data map[interface{}]interface{}
// route controller info
controllerName string
actionName string
methodMapping map[string]func() //method:routertree
gotofunc string
AppController interface{}
// template data
TplName string
ViewPath string
Layout string
LayoutSections map[string]string // the key is the section name and the value is the template name
TplPrefix string
TplExt string
EnableRender bool
// xsrf data
_xsrfToken string
XSRFExpire int
EnableXSRF bool
// session
CruSession session.Store
}
// ControllerInterface is an interface to uniform all controller handler.
type ControllerInterface interface {
Init(ct *context.Context, controllerName, actionName string, app interface{})
Prepare()
Get()
Post()
Delete()
Put()
Head()
Patch()
Options()
Finish()
Render() error
XSRFToken() string
CheckXSRFCookie() bool
HandlerFunc(fn string) bool
URLMapping()
}
beego.Controller
實(shí)現(xiàn)了接口 beego.ControllerInterface
仗岸,beego.ControllerInterface
定義了如下函數(shù):
-
Init(ct *context.Context, childName string, app interface{})
這個(gè)函數(shù)主要初始化了 Context允耿、相應(yīng)的 Controller 名稱借笙,模板名扒怖,初始化模板參數(shù)的容器 Data,app 即為當(dāng)前執(zhí)行的 Controller 的 reflecttype业稼,這個(gè) app 可以用來執(zhí)行子類的方法盗痒。
-
Prepare()
這個(gè)函數(shù)主要是為了用戶擴(kuò)展用的,這個(gè)函數(shù)會(huì)在下面定義的這些 Method 方法之前執(zhí)行低散,用戶可以重寫這個(gè)函數(shù)實(shí)現(xiàn)類似用戶驗(yàn)證之類俯邓。
-
Get()
如果用戶請(qǐng)求的 HTTP Method 是 GET,那么就執(zhí)行該函數(shù)熔号,默認(rèn)是 405稽鞭,用戶繼承的子 struct 中可以實(shí)現(xiàn)了該方法以處理 Get 請(qǐng)求。
-
Post()
如果用戶請(qǐng)求的 HTTP Method 是 POST引镊,那么就執(zhí)行該函數(shù)朦蕴,默認(rèn)是 405,用戶繼承的子 struct 中可以實(shí)現(xiàn)了該方法以處理 Post 請(qǐng)求弟头。
-
Delete()
如果用戶請(qǐng)求的 HTTP Method 是 DELETE,那么就執(zhí)行該函數(shù),默認(rèn)是 405汰瘫,用戶繼承的子 struct 中可以實(shí)現(xiàn)了該方法以處理 Delete 請(qǐng)求箕憾。
-
Put()
如果用戶請(qǐng)求的 HTTP Method 是 PUT,那么就執(zhí)行該函數(shù)伦连,默認(rèn)是 405雨饺,用戶繼承的子 struct 中可以實(shí)現(xiàn)了該方法以處理 Put 請(qǐng)求.
-
Head()
如果用戶請(qǐng)求的 HTTP Method 是 HEAD,那么就執(zhí)行該函數(shù)惑淳,默認(rèn)是 405额港,用戶繼承的子 struct 中可以實(shí)現(xiàn)了該方法以處理 Head 請(qǐng)求。
-
Patch()
如果用戶請(qǐng)求的 HTTP Method 是 PATCH汛聚,那么就執(zhí)行該函數(shù)锹安,默認(rèn)是 405,用戶繼承的子 struct 中可以實(shí)現(xiàn)了該方法以處理 Patch 請(qǐng)求.
-
Options()
如果用戶請(qǐng)求的HTTP Method是OPTIONS倚舀,那么就執(zhí)行該函數(shù)叹哭,默認(rèn)是 405,用戶繼承的子 struct 中可以實(shí)現(xiàn)了該方法以處理 Options 請(qǐng)求痕貌。
-
Finish()
這個(gè)函數(shù)是在執(zhí)行完相應(yīng)的 HTTP Method 方法之后執(zhí)行的风罩,默認(rèn)是空,用戶可以在子 struct 中重寫這個(gè)函數(shù)舵稠,執(zhí)行例如數(shù)據(jù)庫關(guān)閉超升,清理數(shù)據(jù)之類的工作入宦。
-
Render() error
這個(gè)函數(shù)主要用來實(shí)現(xiàn)渲染模板,如果 beego.AutoRender 為 true 的情況下才會(huì)執(zhí)行室琢。
所以通過子 struct 的方法重寫乾闰,用戶就可以實(shí)現(xiàn)自己的邏輯,接下來我們看一個(gè)實(shí)際的例子:
type AddController struct {
beego.Controller
}
func (this *AddController) Prepare() {
}
func (this *AddController) Get() {
this.Data["content"] = "value"
this.Layout = "admin/layout.html"
this.TplName = "admin/add.tpl"
}
func (this *AddController) Post() {
pkgname := this.GetString("pkgname")
content := this.GetString("content")
pk := models.GetCruPkg(pkgname)
if pk.Id == 0 {
var pp models.PkgEntity
pp.Pid = 0
pp.Pathname = pkgname
pp.Intro = pkgname
models.InsertPkg(pp)
pk = models.GetCruPkg(pkgname)
}
var at models.Article
at.Pkgid = pk.Id
at.Content = content
models.InsertArticle(at)
this.Ctx.Redirect(302, "/admin/index")
}
從上面的例子可以看出來盈滴,通過重寫方法可以實(shí)現(xiàn)對(duì)應(yīng) method 的邏輯涯肩,實(shí)現(xiàn) RESTful 結(jié)構(gòu)的邏輯處理。
Controller中數(shù)據(jù)參數(shù)處理
獲取參數(shù)
我們經(jīng)常需要獲取用戶傳遞的數(shù)據(jù)巢钓,包括 Get病苗、POST 等方式的請(qǐng)求,beego 里面會(huì)自動(dòng)解析這些數(shù)據(jù)症汹,你可以通過如下方式獲取數(shù)據(jù):
- GetString(key string) string
- GetStrings(key string) []string
- GetInt(key string) (int64, error)
- GetBool(key string) (bool, error)
- GetFloat(key string) (float64, error)
使用例子如下:
func (c *InputController) Get() {
id := c.GetString("id")
//c.GetStrings() 數(shù)組
//c.Input().Get()
c.Ctx.WriteString("id:" + id)
}
如果你需要的數(shù)據(jù)可能是其他類型的硫朦,例如是 int 類型而不是 int64,那么你需要這樣處理:
func (this *MainController) Post() {
id := this.Input().Get("id")
intid, err := strconv.Atoi(id)
}
更多其他的 request 的信息背镇,用戶可以通過 this.Ctx.Request
獲取信息咬展,關(guān)于該對(duì)象的屬性和方法參考手冊(cè) Request。
直接解析到 struct
如果要把表單里的內(nèi)容賦值到一個(gè) struct 里芽世,除了用上面的方法一個(gè)一個(gè)獲取再賦值外挚赊,beego 提供了通過另外一個(gè)更便捷的方式,就是通過 struct 的字段名或 tag 與表單字段對(duì)應(yīng)直接解析到 struct济瓢。
定義 struct:
type User struct {
Username string
Password string
}
表單:
func (c *InputController) Get() {
name := c.GetSession("name")
if name != "" {}
c.Ctx.WriteString(`<html><form action="http://127.0.0.1:8080/input" method="post">
<input type="text" name="Username"/>
<input type="password" name="Password"/>
<input type="submit" value="提交"/>
</form></html>`)
}
Controller 里解析:
func (c *InputController) Post() {
u := User{}
if err := c.ParseForm(&u); err != nil {
}
c.Ctx.WriteString("Username:" + u.Username + "password:" + u.Password)
}
注意:
- StructTag form 的定義和 renderform方法 共用一個(gè)標(biāo)簽
- 定義 struct 時(shí)荠割,字段名后如果有 form 這個(gè) tag,則會(huì)以把 form 表單里的 name 和 tag 的名稱一樣的字段賦值給這個(gè)字段旺矾,否則就會(huì)把 form 表單里與字段名一樣的表單內(nèi)容賦值給這個(gè)字段蔑鹦。如上面例子中,會(huì)把表單中的 username 和 age 分別賦值給 user 里的 Name 和 Age 字段箕宙,而 Email 里的內(nèi)容則會(huì)賦給 Email 這個(gè)字段嚎朽。
- 調(diào)用 Controller ParseForm 這個(gè)方法的時(shí)候,傳入的參數(shù)必須為一個(gè) struct 的指針柬帕,否則對(duì) struct 的賦值不會(huì)成功并返回
xx must be a struct pointer
的錯(cuò)誤哟忍。 - 如果要忽略一個(gè)字段,有兩種辦法陷寝,一是:字段名小寫開頭锅很,二是:
form
標(biāo)簽的值設(shè)置為-
獲取 Request Body 里的內(nèi)容
在 API 的開發(fā)中,我們經(jīng)常會(huì)用到 JSON
或 XML
來作為數(shù)據(jù)交互的格式凤跑,如何在 beego 中獲取 Request Body 里的 JSON 或 XML 的數(shù)據(jù)呢爆安?
- 在配置文件里設(shè)置
copyrequestbody = true
- 在 Controller 中
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()
}
文件上傳
在 beego 中你可以很容易的處理文件上傳,就是別忘記在你的 form 表單中增加這個(gè)屬性 enctype="multipart/form-data"
仔引,否則你的瀏覽器不會(huì)傳輸你的上傳文件扔仓。
文件上傳之后一般是放在系統(tǒng)的內(nèi)存里面褐奥,如果文件的 size 大于設(shè)置的緩存內(nèi)存大小,那么就放在臨時(shí)文件中翘簇,默認(rèn)的緩存內(nèi)存是 64M撬码,你可以通過如下來調(diào)整這個(gè)緩存內(nèi)存大小:
beego.MaxMemory = 1<<22
或者在配置文件中通過如下設(shè)置:
maxmemory = 1<<22
Beego 提供了兩個(gè)很方便的方法來處理文件上傳:
-
GetFile(key string) (multipart.File, *multipart.FileHeader, error)
該方法主要用于用戶讀取表單中的文件名
the_file
,然后返回相應(yīng)的信息缘揪,用戶根據(jù)這些變量來處理文件上傳:過濾耍群、保存文件等义桂。 -
SaveToFile(fromfile, tofile string) error
該方法是在 GetFile 的基礎(chǔ)上實(shí)現(xiàn)了快速保存的功能
fromfile 是提交時(shí)候的 html 表單中的 name
<form enctype="multipart/form-data" method="post">
<input type="file" name="uploadname" />
<input type="submit">
</form>
保存的代碼例子如下:
func (c *FormController) Post() {
f, h, err := c.GetFile("uploadname")
if err != nil {
log.Fatal("getfile err ", err)
}
defer f.Close()
c.SaveToFile("uploadname", "static/upload/" + h.Filename) // 保存位置在 static/upload, 沒有文件夾要先創(chuàng)建
}
數(shù)據(jù)綁定
支持從用戶請(qǐng)求中直接數(shù)據(jù) bind 到指定的對(duì)象,例如請(qǐng)求地址如下
?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie
var id int
this.Ctx.Input.Bind(&id, "id") //id ==123
var isok bool
this.Ctx.Input.Bind(&isok, "isok") //isok ==true
var ft float64
this.Ctx.Input.Bind(&ft, "ft") //ft ==1.2
ol := make([]int, 0, 2)
this.Ctx.Input.Bind(&ol, "ol") //ol ==[1 2]
ul := make([]string, 0, 2)
this.Ctx.Input.Bind(&ul, "ul") //ul ==[str array]
user struct{Name}
this.Ctx.Input.Bind(&user, "user") //user =={Name:"astaxie"}