Skip to content

Latest commit

 

History

History
573 lines (509 loc) · 17.7 KB

04.md

File metadata and controls

573 lines (509 loc) · 17.7 KB

控制器 - controller

###导航:

  1. 操作流程
  2. 添加控制器和使用
  3. 指针控制器与值控制器
  4. 控制器函数
  5. 控制器参数
  6. 结构参数封装
  7. 控制器返回值
  8. Before与After控制器函数
  9. 重定向与控制器转发

操作流程

控制器与函数的验证->解析From、Query、Files参数->调用before函数->调用请求函数->解析请求函数的返回值->调用after函数

添加控制器和使用

Leafveingo添加控制器非常的方便,可根据自己业务进行router key的设置,每个router key代表一个控制器。

代码实例:

type MainController struct {
	tag string // 主要为了区别struct不同的内存地址
}

//	控制器的默认请求访问的函数(Index),URL结尾检测为"/"( http://localhost:8080/ )
func (m *MainController) Index() string {
	return "Hello world, Leafvingo web framework"
}

...

leafvein := leafveingo.SharedLeafvein()

//	原型:AddController(routerKey string, controller interface{})

//	值类型控制器添加
leafvein.AddController("/", MainController{})

//	特别说明:URL的访问规则 - AddController("router key",控制器{})
//	http://localhost:8080/[控制器router key][控制器函数]
//
//	URL规则请求例子:
//	控制器的函数名 = "User"(默认index)
//	router  key  = "/admin"  = http://localhost:8080/adminuser
//	router  key  = "/admin/" = http://localhost:8080/admin/user
//
//	控制器的函数名 = ""(默认index)
//	router  key  = "/admin"  = http://localhost:8080/adminindex
//	router  key  = "/admin/" = http://localhost:8080/admin/index
//	router  key  = "/" 		 = http://localhost:8080/

指针控制器与值控制器

指针控制器:每次请求直接使用AddController("/", &MainController{})添加控制器的地址进行函数的调用 值控制器:每次请求都会根据AddController("/", MainController{})添加控制器的类型新建立一个对象进行操作

指针控制器和值控制器的使用取决在于对高并发处理和需求方案而定,由于指针控制器每个请求都使用同一个控制器的内存地址,所以对高并发处理有比较高的要求。 所以两者的使用可以根据项目的情况和处理机制来进行选择。

控制器函数

控制器的函数可以随便定义,如果希望URL请求能访问到的函数需要首字母大写,关于控制器URL的解析可以转到路由器机制-控制器函数解析中查看,根据请求(Request.Method)定义自己需要访问到控制器的函数。
控制器默认的Index函数每个控制器都可以有一个Index函数,也可以不定义,如果说想修改Index这个名称可以改下源码的CONTROLLER_DEFAULT_METHOD的常量参数。

控制器参数

Leafveingo对参数的封装做了很灵活的处理,想要获取那个对象直接声明即可,随意组合参数列表。

控制器参数支持类型:

  • *http.Request
  • http.ResponseWriter
  • leafveingo.HttpContext
  • *leafveingo.HttpContext
  • LVSession.HttpSession
  • struct

代码实例(/trunk/src/github.com/slowfei/leafveingo/example/sample/src/controllers/params_controller.go):


type MainController struct {
	tag string // 主要为了区别struct不同的内存地址
}

//	获取 *http.Request 和 http.ResponseWriter参数
func (m *MainController) Param1(request *http.Request, rw http.ResponseWriter) string {
	return fmt.Sprintf("Request:%v \n\n ResponseWriter:%p", request, rw)
}

//	获取session
func (m *MainController) Param2(session LVSession.HttpSession) string {
	return fmt.Sprintf("HttpSession:%p", session)
}

//	获取上下文HttpContext
func (m *MainController) Param3(context *leafveingo.HttpContext) string {
	return fmt.Sprintf("HttpContext:%p", context)
}

结构参数封装

Leafveingo对自定义的参数获取也做了处理,使用struct封装参数列表可以快速简洁的获取需要的参数信息。

首先需要定义一个参数的结构或直接再函数的参数中定义,代码如下:

  func (m *MainController) Param4(params struct {
  	Id   string // id 切记首字母大写,其他与参数匹配 Id = id, ID = ID | iD
  	Name string // name
  }) {
  	return fmt.Sprintf("id=%v, name=%v", params.Id, params.Name)
  }

也可以这样,代码如下:

  type UserInfo struct {
  	Id   string
  	Name string
  }

  func (m *MainController) Param5(params UserInfo) {
  	return fmt.Sprintf("id=%v, name=%v", params.Id, params.Name)
  }

参数需要一个结构对象来存储,以上的均对应参数?id=1&name=slowfei,from表单提交的参数也是一样的。注意注意:表单参数需要PostParam5的函数命名

####嵌套结构参数封装 嵌套结构以from作为例子,代码如下:

  type UserType struct {
  	TypeName string //	用户的类型名称
  }
  type User struct {
  	Uid      int      // 用户ID
  	UserName string   // 用户名称
  	Type     UserType // 用户类型
  	Interest []string // 用户的兴趣爱好,集合参数
  }

  func (m *MainController) Param6() interface{} {
  	//	参数名设置注意查看input的name
  	bodyHTML := `
  <!doctype html>
  <html>
  	<meta charset="UTF-8">
  	<head>
  		<title>嵌套参数结构封装</title>
  	</head>
  	<body>
  		<form action="param6.htm" method="post">
  			<lable>用户ID</label>
  			<input type="text" name="uid" value="1"/>
  			<br/>

  			<lable>用户名称</label>
  			<input type="text" name="userName" value="slowfei"/>
  			<br/>

  			<lable>用户类型(name="type.typeName" 对应结构体的字段名称)</label>
  			<input type="text" name="type.typeName" value="admin"/>
  			<br/>

  			<input type="submit" value="提交" />
  		</form>
  	</body>
  </html>
  	`
  
  	return leafveingo.BodyHtml(bodyHTML)
  }

  //	post 请求打印参数
  func (m *MainController) PostParam6(params User) string {
  	return fmt.Sprintf("uid=%v\nuserName=%v\ntype=%v", 
  			params.Uid, params.UserName, params.Type.TypeName)
  }

####数组集合参数封装 数组参数主要用[index]作为标识,也可以嵌套集合,代码如下:

  type UserBean struct {
  	Users []User
  	Tags  []string
  }

  //	页面请求
  func (p *ParamsController) Param7() leafveingo.HtmlOut {
  	//	参数名设置注意查看input的name
  	bodyHTML := `
  <!doctype html>
  <html>
  	<meta charset="UTF-8">
  	<head>
  		<title>嵌套参数结构封装</title>
  	</head>
  	<body>
  		<form action="param7.htm" method="post">
  			<h3>用户1</h3>
  			<lable>用户ID(name="users[0].uid"): 用户名称(name="users[0].userName"): 
  				   用户类型(name="users[0].type.typeName")</label>
  			<br/>
  			<input type="text" name="users[0].uid" value="1"/>
  			<input type="text" name="users[0].userName" value="slowfei_1"/>
  			<input type="text" name="users[0].type.typeName" value="admin"/>
  			<br/>
  			<label>兴趣爱好(name="users[0].interest[0]")</label>
  			<br/>
  			<input type="text" name="users[0].interest[0]" value="1_爱好1"/>
  			<input type="text" name="users[0].interest[1]" value="1_爱好2"/>
  			<input type="text" name="users[0].interest[2]" value="1_爱好3"/>
  			<br/>

  			<h3>用户2</h3>
  			<lable>用户ID(name="users[1].uid"): 用户名称(name="users[1].userName"): 
  				   用户类型(name="users[1].type.typeName")</label>
  			<br/>
  			<input type="text" name="users[1].uid" value="2"/>
  			<input type="text" name="users[1].userName" value="slowfei_2"/>
  			<input type="text" name="users[1].type.typeName" value="admin"/>
  			<br/>
  			<label>兴趣爱好(name="users[1].interest[0]")</label>
  			<br/>
  			<input type="text" name="users[1].interest[0]" value="2_爱好1"/>
  			<input type="text" name="users[1].interest[1]" value="2_爱好2"/>
  			<input type="text" name="users[1].interest[2]" value="2_爱好3"/>
  			<br/>
  			
  			<h3>用户3</h3>
  			<lable>用户ID(name="users[2].uid"): 用户名称(name="users[2].userName"): 
  				   用户类型(name="users[2].type.typeName")</label>
  			<br/>
  			<input type="text" name="users[2].uid" value="3"/>
  			<input type="text" name="users[2].userName" value="slowfei_3"/>
  			<input type="text" name="users[2].type.typeName" value="admin"/>
  			<br/>
  			<label>兴趣爱好(name="users[2].interest[0]")</label>
  			<br/>
  			<input type="text" name="users[2].interest[0]" value="3_爱好1"/>
  			<input type="text" name="users[2].interest[1]" value="3_爱好2"/>
  			<input type="text" name="users[2].interest[2]" value="3_爱好3"/>
  			<br/>

  			<h3>Tags</h3>
  			tags1(name="tags"):
  			<input type="checkbox" name="tags" value="tags1">
  			tags2(name="tags"):
  			<input type="checkbox" name="tags" value="tags2">
  			tags3(name="tags"):
  			<input type="checkbox" name="tags" value="tags3">
  			<br/>

  			<input type="submit" value="提交" />
  		</form>
  	</body>
  </html>
  	`

  	return leafveingo.BodyHtml(bodyHTML)
  }

  //	post 打印集合参数
  func (p *ParamsController) PostParam7(params UserBean) string {
  	buf := bytes.NewBufferString("")

  	buf.WriteString(fmt.Sprintf("Tags: %v \n\n", params.Tags))

  	buf.WriteString(fmt.Sprintf("用户数量:%v \n", len(params.Users)))
  	for i, v := range params.Users {
  		buf.WriteString(fmt.Sprintf("用户%v: %v \n", i, v))
  	}

  	return buf.String()
  }

控制器返回值

每一次控制器函数处理后的结果是针对性的响应body作为处理,Leafveingo提供了强大的控制器返回结果处理操作使用非常的方便,可以查看github.com/slowfei/leafveingo/response_body.go具体操作,下面列出常用的API列表:

  response_body.go

  //	输出text ,Content-Type = text/plain
  func BodyText(text string) string
  
  //	输出text html, Content-Type = text/html
  func BodyHtml(html string) HtmlOut

  //	输出json text,Content-Type = application/json
  func BodyJson(value interface{}) (SFJson.Json, error)
  
  //	输出[]byte
  //	@content 	 body content
  //	@contentType 内容类型,"" = "text/plain; charset=utf-8"
  //	@headers	 需要添加头部的其他信息
  func BodyByte(content []byte, contentType string, headers map[string]string) ByteOut

  //	模板输出	Content-Type = text/html
  //	@isCompress 是否进行压缩处理
  //	
  //	模板路径:
  // 	template path: (模板目录)/(controllerRouter)/(methodName).tpl
  //	e.g.:    URL = http://localhost:8080/index
  //	  router key = "/", method "Index"
  //	templatePath = (模板目录)/index.tpl
  //
  //			 URL = http://localhost:8080/Admin/index
  //	  router key  = "/admin/", method "Index"
  //	templatePath = (模板目录)/admin/index.tpl
  func BodyTemplate(data interface{}) LVTemplate.TemplateValue
  
  //	可自定义模板路径
  //	custom template path: (模板目录)/(tplPath).tpl
  //	e.g.: tplPath = "custom/base.tpl"
  //	templatePath  = (模板目录)/custom/base.tpl
  func BodyTemplateByTplPath(tplPath string, data interface{}) LVTemplate.TemplateValue

  //	重定向URL
  func BodyRedirect(url string) Redirect
  
  //	控制器转发(具体可以查看{#重定向与控制器转发})
  func BodyCellController(routerKey, methodName string) Dispatcher
  func BodyCellControllerByHeaders(routerKey, methodName string, setHeaders map[string]string) Dispatcher
  
  //	文件输出服务
  func BodyServeFile(path string) ServeFilePath
   
   .

代码实例(github.com/slowfei/leafveingo/example/sample/src/controllers/returnparam_controller.go):

import (
	"fmt"
	"github.com/slowfei/gosfcore/encoding/json"
	"github.com/slowfei/leafveingo"
)

//	控制器返回参数演示
type ReturnParamController struct {
	tag string
}

//	输出 text, Content-Type = text/plain
func (r *ReturnParamController) Text() string {
	// return "return text"
	return leafveingo.BodyText("rreturn text")
}

// 输出text html, Content-Type = text/plain
func (r *ReturnParamController) Html() leafveingo.HtmlOut {
	html := `
		<!doctype html>
		<html lang="en">
		<head>
			<meta charset="UTF-8">
			<title>Document</title>
		</head>
		<body>
			<h1>Hello world</h1>
		</body>
		</html>
	`
	return leafveingo.BodyHtml(html)
}

//	输出json Content-Type = application/json
func (r *ReturnParamController) Json(params struct {
	T int
}) interface{} {
	type TStruct struct {
		ID   string
		Name string
	}
	t := TStruct{"1", "slowfei"}
	t2 := TStruct{"2", "slowfei_2"}

	var j SFJson.Json
	var e error

	if params.T == 1 {
		j, e = leafveingo.BodyJson([]TStruct{t, t2})
	} else {
		j, e = leafveingo.BodyJson(t)
	}

	if nil != e {
		return e.Error()
	}
	return j
}

//	输出 []byte
func (r *ReturnParamController) Byte() leafveingo.ByteOut {
	return leafveingo.Bodybyte([]byte("hello world []byte"), true, "text/plain; charset=utf-8", nil)
}

//	输出模板
func (r *ReturnParamController) Template(params struct {
	Info string
	T    int
}) interface{} {
	if params.T == 1 {
		return leafveingo.BodyTemplateByTplPath("custom/custom.tpl", params.Info)
	} else {
		// 模板默认路径,根据router key 命名文件夹然后函数名指定模板名称
		// github.com/slowfei/leafveingo/example/sample/SampleWeb/template/r/template.tpl
		return leafveingo.BodyTemplate(params.Info)
	}

}

//	重定向url
func (r *ReturnParamController) Redirect(params struct {
	Url string
}) interface{} {
	if 0 == len(params.Url) {
		return leafveingo.BodyHtml(`
			<!doctype html>
			<html lang="en">
			<head>
				<meta charset="UTF-8">
				<title>Redirect</title>
			</head>
			<body>
				<a href="/r/redirect.htm?url=http://www.google.com">Redirect to google</a>
			</body>
			</html>
			`)
	} else {
		return leafveingo.BodyRedirect(params.Url)
	}
}

//	控制器转发
func (r *ReturnParamController) Dispatcher(params struct {
	Info string
}) interface{} {
	//	注意router key,对应添加控制器router
	return leafveingo.BodyCellController("/r/", "DispTest")
}
func (r *ReturnParamController) DispTest(params struct {
	Info string
}) string {
	return fmt.Sprintf("Dispatcher to DispTest Info = %v", params.Info)
}

//	文件输出
func (r *ReturnParamController) File() interface{} {
	//	webRoot目录为标准
	//	github.com/slowfei/leafveingo/example/sample/SampleWeb/webRoot/file.zip
	return leafveingo.BodyServeFile("file.zip")
}

Before与After控制器函数

Before与After是在请求控制器函数前和后进行调用,可以作为整个控制器访问权限或则一些业务需要的处理。使用也和简单,只要在控制起加入特定的函数就可以了。

  // before method
  func (ba *BeforeAfterController) Before()
  // after method
  func (ba *BeforeAfterController) After()

请求流程:

  • 请求/ba/index.htm -> 调用Before -> 返回 false -> 拒绝访问跳转403 -> 调用After函数。
  • 请求/ba/index.htm -> 调用Before -> 返回 true -> 调用Index函数 -> 调用After函数。

代码实例(/src/github.com/slowfei/leafveingo/example/sample/src/controllers/before_after_controller.go):

import (
	"fmt"
	"net/http"
)

//	before after 演示控制器
type BeforeAfterController struct {
	tag string
}

//	before
//	可接收的参数:*http.Request、*url.URL、
//				*leafveingo.HttpContext、[]uint8(Request.Body)
//				http.ResponseWriter、LVSession.HttpSession
//	@return bool 返回true 可继续访问请求的函数,false 则跳转403错误页面; 默认可以设置返回值等于true
func (ba *BeforeAfterController) Before(req *http.Request,rw http.ResponseWriter) bool {
	if "123456" == req.URL.Query().Get("pwd") {
		return true
	} else {
		return false
	}
}

// index
func (ba *BeforeAfterController) Index() string {
	return "Wish you a successful visit!"
}

//	after
//	可接收的参数:*http.Request、*url.URL、
//				*leafveingo.HttpContext、[]uint8(Request.Body)
//				http.ResponseWriter、LVSession.HttpSession
func (ba *BeforeAfterController) After(req *http.Request) {
	fmt.Printf("request end (%v) \n", req.URL.String())
}

重定向与控制器转发

重定向转发使用的是http.Redirect(w,r,code)实现的,没有什么特别直接使用如下代码作为控制器返回值即可。

  return leafveingo.BodyRedirect(url)

控制器转发时需要输入正确的router key和控制器函数名,不然router key找不到会抛出500错误,函数名找不到404错误。

  return leafveingo.BodyCellController(router key, controller method name)

相关代码可以查看 github.com/slowfei/leafveingo/example/sample/src/controllers/returnparam_controller.go

代码案例:

//	重定向url
func (r *ReturnParamController) Redirect(params struct {
	Url string
}) interface{} {
	if 0 == len(params.Url) {
		return leafveingo.BodyHtml(`
			<!doctype html>
			<html lang="en">
			<head>
				<meta charset="UTF-8">
				<title>Redirect</title>
			</head>
			<body>
				<a href="/r/redirect.htm?url=http://www.google.com">Redirect to google</a>
			</body>
			</html>
			`)
	} else {
		return leafveingo.BodyRedirect(params.Url)
	}
}

//	控制器转发
func (r *ReturnParamController) Dispatcher(params struct {
	Info string
}) interface{} {
	//	注意router key,对应添加控制器router
	return leafveingo.BodyCellController("/r/", "DispTest")
}
func (r *ReturnParamController) DispTest(params struct {
	Info string
}) string {
	return fmt.Sprintf("Dispatcher to DispTest Info = %v", params.Info)
}