Files
build-web-application-with-…/zh-tw/13.3.md
2019-02-26 01:40:54 +08:00

6.4 KiB
Raw Blame History

13.3 controller設計

傳統的MVC框架大多數是基於Action設計的字尾式對映然而現在Web流行REST風格的架構。儘管使用Filter或者rewrite能夠透過URL重寫實現REST風格的URL但是為什麼不直接設計一個全新的REST風格的 MVC框架呢本小節就是基於這種思路來講述如何從頭設計一個基於REST風格的MVC框架中的controller最大限度地簡化Web應用的開發甚至編寫一行程式碼就可以實現“Hello, world”。

controller作用

MVC設計模式是目前Web應用開發中最常見的架構模式透過分離 Model模型、View檢視和 Controller控制器可以更容易實現易於擴充套件的使用者介面(UI)。Model指後臺返回的資料View指需要渲染的頁面通常是範本頁面渲染後的內容通常是HTMLController指Web開發人員編寫的處理不同URL的控制器如前面小節講述的路由就是URL請求轉發到控制器的過程controller在整個的MVC框架中起到了一個核心的作用負責處理業務邏輯因此控制器是整個框架中必不可少的一部分Model和View對於有些業務需求是可以不寫的例如沒有資料處理的邏輯處理沒有頁面輸出的302調整之類別的就不需要Model和View但是controller這一環節是必不可少的。

beego的REST設計

前面小節介紹了路由實現了註冊struct的功能而struct中實現了REST方式因此我們需要設計一個用於邏輯處理controller的基底類別這裡主要設計了兩個型別一個struct、一個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)    //初始化上下文和子類別名稱稱
	Prepare()                       //開始執行之前的一些處理
	Get()                           //method=GET的處理
	Post()                          //method=POST的處理
	Delete()                        //method=DELETE的處理
	Put()                           //method=PUT的處理
	Head()                          //method=HEAD的處理
	Patch()                         //method=PATCH的處理
	Options()                       //method=OPTIONS的處理
	Finish()                        //執行完成之後的處理		
	Render() error                  //執行完method對應的方法之後渲染頁面
}

那麼前面介紹的路由add函式的時候是定義了ControllerInterface型別因此只要我們實現這個介面就可以所以我們的基底類別Controller實現如下的方法


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基底類別已經實現了介面定義的函式透過路由根據url執行相應的controller的原則會依次執行如下


Init()      初始化
Prepare()   執行之前的初始化每個繼承的子類別可以來實現該函式
method()    根據不同的method執行不同的函式GETPOSTPUTHEAD等子類別來實現這些函式如果沒實現那麼預設都是403
Render()    可選根據全域性變數AutoRender來判斷是否執行
Finish()    執行完之後執行的操作每個繼承的子類別可以來實現該函式

應用指南

上面beego框架中完成了controller基底類別的設計那麼我們在我們的應用中可以這樣來設計我們的方法


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

上面的方式我們實現了子類別MainController實現了Get方法那麼如果使用者透過其他的方式(POST/HEAD等)來訪問該資源都將返回405而如果是Get來訪問因為我們設定了AutoRender=true那麼在執行完Get方法之後會自動執行Render函式就會顯示如下介面

index.tpl的程式碼如下所示我們可以看到資料的設定和顯示都是相當的簡單方便


<!DOCTYPE html>
<html>
  <head>
    <title>beego welcome template</title>
  </head>
  <body>
    <h1>Hello, world!{{.Username}},{{.Email}}</h1>
  </body>
</html>