模板
目标
服务端渲染
模板可以理解为事先定义好的HTML文档文件,模板渲染的作用机制可以简单理解为文本替换操作–使用相应的数据去替换HTML文档中事先准备好的标记。
前后端分离的开发模式:即 Web 后端提供 RESTful 接口,返回结构化的数据(通常为 JSON 或者 XML)。前端使用 AJAX 技术请求到所需的数据,利用 JavaScript 进行渲染。
前后端分离开发模式的优势:前后端解耦,前端注于界面设计实现,只需要考虑拿到数据后如何渲染即可。后端专心解决资源利用,并发,数据库等问题,只需要考虑数据如何生成。同一套后端服务能够同时支撑小程序、移动APP、PC端 Web 页面,以及对外提供的接口。
设计思路
处理静态文件
将所有的静态文件放在/usr/web
目录下,映射到真实的文件后,将文件返回。
返回:通过net/http
库解析请求的地址,映射到服务器上文件的真实地址,交给http.FileServer
处理。
gee.go
Static
这个方法:用户可以将磁盘上的某个文件夹root
映射到路由relativePath
。
1 2 3 4 5
| r := gee.New() r.Static("/assets", "/usr/jpc/blog/static")
r.Run(":9999")
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc { absolutePath := path.Join(group.prefix, relativePath) fileServer := http.StripPrefix(absolutePath, http.FileServer(fs)) return func(c *Context) { file := c.Param("filepath") if _, err := fs.Open(file); err != nil { c.Status(http.StatusNotFound) return }
fileServer.ServeHTTP(c.Writer, c.Req) } }
func (group *RouterGroup) Static(relativePath string, root string) { handler := group.createStaticHandler(relativePath, http.Dir(root)) urlPattern := path.Join(relativePath, "/*filepath") group.GET(urlPattern, handler) }
|
HTML 模板渲染
go语言提供了html/template
模板库,为 HTML 提供了较为完整的支持。包括普通变量渲染、列表渲染、对象渲染等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Engine struct { *RouterGroup router *router groups []*RouterGroup htmlTemplates *template.Template funcMap template.FuncMap }
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) { engine.funcMap = funcMap }
func (engine *Engine) LoadHTMLGlob(pattern string) { engine.htmlTemplates = template.Must(template.New("").Funcs(engine.funcMap).ParseGlob(pattern)) }
|
context.go支持根据模板文件名选择模板进行渲染。
1 2 3 4 5 6 7 8 9 10 11 12 13
| type Context struct { engine *Engine }
func (c *Context) HTML(code int, name string, data interface{}) { c.SetHeader("Content-Type", "text/html") c.Status(code) if err := c.engine.htmlTemplates.ExecuteTemplate(c.Writer, name, data); err != nil { c.Fail(500, err.Error()) } }
|
gee.go实例化 Context 时,还需要给 c.engine
赋值。
1 2 3 4 5 6 7
| func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { c := newContext(w, req) c.handlers = middlewares c.engine = engine engine.router.handle(c) }
|
main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package main
import ( "fmt" "html/template" "net/http" "time"
"gee" )
type student struct { Name string Age int8 }
func FormatAsDate(t time.Time) string { year, month, day := t.Date() return fmt.Sprintf("%d-%02d-%02d", year, month, day) }
func main() { r := gee.New() r.Use(gee.Logger()) r.SetFuncMap(template.FuncMap{ "FormatAsDate": FormatAsDate, }) r.LoadHTMLGlob("templates/*") r.Static("/assets", "./static")
stu1 := &student{Name: "jpc", Age: 20} stu2 := &student{Name: "Jack", Age: 22} r.GET("/", func(c *gee.Context) { c.HTML(http.StatusOK, "css.tmpl", nil) }) r.GET("/students", func(c *gee.Context) { c.HTML(http.StatusOK, "arr.tmpl", gee.H{ "title": "gee", "stuArr": [2]*student{stu1, stu2}, }) })
r.GET("/date", func(c *gee.Context) { c.HTML(http.StatusOK, "custom_func.tmpl", gee.H{ "title": "gee", "now": time.Date(2019, 8, 17, 0, 0, 0, 0, time.UTC), }) })
r.Run(":9999") }
|
访问页面
项目地址
项目地址