Merging other languages
This commit is contained in:
331
en/13.3.md
331
en/13.3.md
@@ -1,163 +1,168 @@
|
||||
# 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指需要渲染的页面,通常是模板页面,渲染后的内容通常是HTML;Controller指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执行不同的函数:GET、POST、PUT、HEAD等,子类来实现这些函数,如果没实现,那么默认都是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等)来访问该资源都将返回403,而如果是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>
|
||||
|
||||
|
||||
## links
|
||||
* [目录](<preface.md>)
|
||||
* 上一章: [自定义路由器设计](<13.2.md>)
|
||||
* 下一节: [日志和配置设计](<13.4.md>)
|
||||
# 13.3 Design controllers
|
||||
|
||||
Most of the traditional MVC framework is based on the design of postfix Action mapping, however, is now popular REST-style Web architecture. Although the use of Filter or rewrite URL rewriting can be achieved through a REST-style URL, but why not just design a new REST-style MVC framework it ? This section is based on this idea on how to start from scratch to design a REST-style MVC framework based on the controller, to maximize simplify Web application development, or even write a single line of code to achieve the "Hello, world".
|
||||
|
||||
## Controller role
|
||||
|
||||
MVC design pattern is the most common Web application development framework model, by separating Model( model ), View( view ) and the Controller( controller ) can be more easily achieved easily extensible user interface(UI). Model refers to the background data returned ; View refers to the need to render the page, usually a template page, the content is usually rendered HTML; Controller refers to Web developers to write controllers handle different URL, such as the route described in the previous section is a URL request forwarded to the process controller, controller in the whole MVC framework plays a central role, responsible for handling business logic, so the controller is an essential part of the whole framework, Model and View for some business needs can not write, for example, no data processing logic processing, no page output 302 and the like do not need to adjust the Model and View, but the controller of this part is essential.
|
||||
|
||||
## Beego the REST design
|
||||
|
||||
Previous section describes the routing function to achieve a registered struct, and the struct implements REST approach, so we need to design a controller for logic processing base class, where the main design of the two types, a struct, an 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) //Initialize the context and subclass name
|
||||
Prepare() //some processing before execution begins
|
||||
Get() //method = GET processing
|
||||
Post() //method = POST processing
|
||||
Delete() //method = DELETE processing
|
||||
Put() //method = PUT handling
|
||||
Head() //method = HEAD processing
|
||||
Patch() //method = PATCH treatment
|
||||
Options() //method = OPTIONS processing
|
||||
Finish() //executed after completion of treatment
|
||||
Render() error //method executed after the corresponding method to render the page
|
||||
}
|
||||
|
||||
Then add function described earlier, when a route is defined ControllerInterface type, so long as we can implement this interface, so our base class Controller to achieve the following methods:
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
At the controller base class already implements the interface defined functions performed by routing the appropriate controller according url principles will be followed by implementation of the following:
|
||||
|
||||
Init() initializes
|
||||
Prepare() before the execution of the initialization, each subclass can inherit to implement the function
|
||||
method() depending on the method to perform different functions: GET, POST, PUT, HEAD, etc. sub-classes to implement these functions, if not achieved, then the default is 403
|
||||
Render() optional, according to a global variable to determine whether to execute AutoRender
|
||||
Finish() after the implementation of the action, each subclass inherits the function can be achieved
|
||||
|
||||
## Application guide
|
||||
|
||||
Above beego Framework base class to complete the design of the controller, then we in our application can be to design our approach:
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
The way we achieved above subclass MainController, implements Get method, so if the user through other means(POST/HEAD, etc. ) to access the resource will return 403, and if it is Get access, because we set the AutoRender = true, then play in the implementation of the Get method is performed automatically after the Render function, it will display the following interface:
|
||||
|
||||

|
||||
|
||||
index.tpl code is shown below, we can see the data set and display are quite simple:
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>beego welcome template</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, world!{{.Username}},{{.Email}}</h1>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
## Links
|
||||
|
||||
- [Directory](preface.md)
|
||||
- Previous section: [Customized routers](13.2.md)
|
||||
- Next section: [Logs and configurations](13.4.md)
|
||||
|
||||
Reference in New Issue
Block a user