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".
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.
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. subclasses 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
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>
- Directory
- Previous: custom routerdesign
- Next: logs and configuration design