W3Cschool
恭喜您成為首批注冊(cè)用戶(hù)
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
傳統(tǒng)的MVC框架大多數(shù)是基于A(yíng)ction設(shè)計(jì)的后綴式映射,然而,現(xiàn)在Web流行REST風(fēng)格的架構(gòu)。盡管使用Filter或者rewrite能夠通過(guò)URL重寫(xiě)實(shí)現(xiàn)REST風(fēng)格的URL,但是為什么不直接設(shè)計(jì)一個(gè)全新的REST風(fēng)格的 MVC框架呢?本小節(jié)就是基于這種思路來(lái)講述如何從頭設(shè)計(jì)一個(gè)基于REST風(fēng)格的MVC框架中的controller,最大限度地簡(jiǎn)化Web應(yīng)用的開(kāi)發(fā),甚至編寫(xiě)一行代碼就可以實(shí)現(xiàn)“Hello, world”。
MVC設(shè)計(jì)模式是目前Web應(yīng)用開(kāi)發(fā)中最常見(jiàn)的架構(gòu)模式,通過(guò)分離 Model(模型)、View(視圖)和 Controller(控制器),可以更容易實(shí)現(xiàn)易于擴(kuò)展的用戶(hù)界面(UI)。Model指后臺(tái)返回的數(shù)據(jù);View指需要渲染的頁(yè)面,通常是模板頁(yè)面,渲染后的內(nèi)容通常是HTML;Controller指Web開(kāi)發(fā)人員編寫(xiě)的處理不同URL的控制器,如前面小節(jié)講述的路由就是URL請(qǐng)求轉(zhuǎn)發(fā)到控制器的過(guò)程,controller在整個(gè)的MVC框架中起到了一個(gè)核心的作用,負(fù)責(zé)處理業(yè)務(wù)邏輯,因此控制器是整個(gè)框架中必不可少的一部分,Model和View對(duì)于有些業(yè)務(wù)需求是可以不寫(xiě)的,例如沒(méi)有數(shù)據(jù)處理的邏輯處理,沒(méi)有頁(yè)面輸出的302調(diào)整之類(lèi)的就不需要Model和View,但是controller這一環(huán)節(jié)是必不可少的。
前面小節(jié)介紹了路由實(shí)現(xiàn)了注冊(cè)struct的功能,而struct中實(shí)現(xiàn)了REST方式,因此我們需要設(shè)計(jì)一個(gè)用于邏輯處理controller的基類(lèi),這里主要設(shè)計(jì)了兩個(gè)類(lèi)型,一個(gè)struct、一個(gè)interface
type Controller struct {
Ct *Context
Tpl *template.Template
Data map[interface{}]interface{}
ChildName string
TplNames string
Layout []string
TplExt string
}
type ControllerInterface interface {
Init(ct *Context, cn string) //初始化上下文和子類(lèi)名稱(chēng)
Prepare() //開(kāi)始執(zhí)行之前的一些處理
Get() //method=GET的處理
Post() //method=POST的處理
Delete() //method=DELETE的處理
Put() //method=PUT的處理
Head() //method=HEAD的處理
Patch() //method=PATCH的處理
Options() //method=OPTIONS的處理
Finish() //執(zhí)行完成之后的處理
Render() error //執(zhí)行完method對(duì)應(yīng)的方法之后渲染頁(yè)面
}
那么前面介紹的路由add函數(shù)的時(shí)候是定義了ControllerInterface類(lèi)型,因此,只要我們實(shí)現(xiàn)這個(gè)接口就可以,所以我們的基類(lèi)Controller實(shí)現(xiàn)如下的方法:
func (c *Controller) Init(ct *Context, cn string) {
c.Data = make(map[interface{}]interface{})
c.Layout = make([]string, 0)
c.TplNames = ""
c.ChildName = cn
c.Ct = ct
c.TplExt = "tpl"
}
func (c *Controller) Prepare() {
}
func (c *Controller) Finish() {
}
func (c *Controller) Get() {
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
}
func (c *Controller) Post() {
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
}
func (c *Controller) Delete() {
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
}
func (c *Controller) Put() {
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
}
func (c *Controller) Head() {
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
}
func (c *Controller) Patch() {
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
}
func (c *Controller) Options() {
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
}
func (c *Controller) Render() error {
if len(c.Layout) > 0 {
var filenames []string
for _, file := range c.Layout {
filenames = append(filenames, path.Join(ViewsPath, file))
}
t, err := template.ParseFiles(filenames...)
if err != nil {
Trace("template ParseFiles err:", err)
}
err = t.ExecuteTemplate(c.Ct.ResponseWriter, c.TplNames, c.Data)
if err != nil {
Trace("template Execute err:", err)
}
} else {
if c.TplNames == "" {
c.TplNames = c.ChildName + "/" + c.Ct.Request.Method + "." + c.TplExt
}
t, err := template.ParseFiles(path.Join(ViewsPath, c.TplNames))
if err != nil {
Trace("template ParseFiles err:", err)
}
err = t.Execute(c.Ct.ResponseWriter, c.Data)
if err != nil {
Trace("template Execute err:", err)
}
}
return nil
}
func (c *Controller) Redirect(url string, code int) {
c.Ct.Redirect(code, url)
}
上面的controller基類(lèi)已經(jīng)實(shí)現(xiàn)了接口定義的函數(shù),通過(guò)路由根據(jù)url執(zhí)行相應(yīng)的controller的原則,會(huì)依次執(zhí)行如下:
Init() 初始化
Prepare() 執(zhí)行之前的初始化,每個(gè)繼承的子類(lèi)可以來(lái)實(shí)現(xiàn)該函數(shù)
method() 根據(jù)不同的method執(zhí)行不同的函數(shù):GET、POST、PUT、HEAD等,子類(lèi)來(lái)實(shí)現(xiàn)這些函數(shù),如果沒(méi)實(shí)現(xiàn),那么默認(rèn)都是403
Render() 可選,根據(jù)全局變量AutoRender來(lái)判斷是否執(zhí)行
Finish() 執(zhí)行完之后執(zhí)行的操作,每個(gè)繼承的子類(lèi)可以來(lái)實(shí)現(xiàn)該函數(shù)
上面beego框架中完成了controller基類(lèi)的設(shè)計(jì),那么我們?cè)谖覀兊膽?yīng)用中可以這樣來(lái)設(shè)計(jì)我們的方法:
package controllers
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Data["Username"] = "astaxie"
this.Data["Email"] = "astaxie@gmail.com"
this.TplNames = "index.tpl"
}
上面的方式我們實(shí)現(xiàn)了子類(lèi)MainController,實(shí)現(xiàn)了Get方法,那么如果用戶(hù)通過(guò)其他的方式(POST/HEAD等)來(lái)訪(fǎng)問(wèn)該資源都將返回403,而如果是Get來(lái)訪(fǎng)問(wèn),因?yàn)槲覀冊(cè)O(shè)置了AutoRender=true,那么在執(zhí)行完Get方法之后會(huì)自動(dòng)執(zhí)行Render函數(shù),就會(huì)顯示如下界面:
index.tpl的代碼如下所示,我們可以看到數(shù)據(jù)的設(shè)置和顯示都是相當(dāng)?shù)暮?jiǎn)單方便:
<!DOCTYPE html>
<html>
<head>
<title>beego welcome template</title>
</head>
<body>
<h1>Hello, world!{{.Username}},{{.Email}}</h1>
</body>
</html>
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話(huà):173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: