本篇文章为大家展示了golang微服务框架中如何扩展go-zero使之支持html模板解析自动化,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
成都创新互联网站建设公司提供网站设计和自适应建站服务。团队由有经验的网页设计师、程序员和市场专家组成,能够提供从H5网站设计,网站制作,一元广告,模板建站到重庆小程序开发公司等全方位服务。 以客户为中心,致力于为客户提供创新、高效的解决方案,帮助您打造成功的企业网站。
go-zero本身支持html模板解析,我们只需要添加url对应模板解hanlder,实现逻辑就可以了
但是winlion太懒了,我甚至想
- 不写任何一个和模板相关的handler 
- 如果有新的模板,直接把模板到某个特定目录就好,不要动任何go代码 
- 在开发环境下没有缓存,修改了模板文件无需重启 
需求在这里,开撸吧
在代码开始前,你可能需要阅读
金光灿灿的Gorm V2+适合创业的golang微服务框架go-zero实战 如果对go-zero已经了解,直接跳过吧
创建项目
生成go.mod文件
以如下指令创建项目
mkdir html cd html go mod init html
定义html.api
本文设计API如下 |描述|格式|方法|参数|返回|是否需要鉴权| |----|----|----|----|----|----| |用户登录|/open/authorization|post|mobile:手机号,passwd:密码,code:图片验证码|id:用户ID,token:用户token|否|
根据以上描述,书写api的模板文件如下
type (
	UserOptReq struct {
		mobile string `form:"mobile"`
		passwd string `form:"passwd"`
		code   string `form:"code,optional"`
	}
	UserOptResp struct {
		id    uint   `json:"id"`
		token string `json:"token"`
	}
)
service html-api {
	@server(
		handler: authorizationHandler
		folder: open
	)
	post /open/authorization(UserOptReq) returns(UserOptResp)
	
}注意
- 本文和html模板相关,可以不适用goctl工具 
- 但是由于使用工具可以为我们节省很多搭建框架相关的工作,所以建议使用用ctl生成 
生成代码
采用如下指令生成代码
goctl api go -api html.api -dir .
此时用go run html.go指令可以发现系统以及运行
html模板自动解析实现思路
模板解析需要了解如下俩个已知知识点
- html网页输出本质上是get请求输出 
- 相对于一个项目来说,模板文件个数是有限的,因此我们可以将模板枚举出来,完成访模板名称和请求之间的映射 
对于第一个,我们可以构建get路由来实现请求,以首页请求http://127.0.0.1:8888/index.html为例,核心代码如下,
	htmltplrouter:= rest.Route{
			Method:  http.MethodGet,
			Path:    "/index.html",
			Handler: htmlhandler(...),
	}
	engine.AddRoute(htmltplrouter)在上述代码中,htmlhandler函数实现了对请求的响应,也就是解析了模板并将模板内容输出
//gloabtemplate:全局解析的模板参数
//tplname:模板名称,
//serverCtx 应用配置
func htmlhandler(gloabtemplate *template.Template, tplname string, serverCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		//模板名字就是r.URL.Path
		t := gloabtemplate
		//如果是调试模式,则支持热解析
		if serverCtx.Config.Debug {
			t, _ = template.New("").Funcs(FuncMap()).ParseGlob(serverCtx.Config.TemplatePattern)
		}
		err := t.ExecuteTemplate(w, tplname, r.URL.Query())
		if err != nil {
			httpx.Error(w, err)
		}
	}
}如何建立uri和模板名称之间的映射关系
这里有几个点需要强调:
- 在golang中,每个包含模板内容的html文件会被解析成一个模板,如在 - view/www/下新建- test.html文件,即使里面没有内容,系统也会将其解析得到一个名叫- test.html的模板。
- 如果在模板文件以template标签中定义名称为 - www/test.html的模板,则系统又会解析得到一个名叫- www/test.html的模板,此时存在俩个模板,一个名叫- test.html,一个名叫- www/test.html
view/www/test.html文件内容如下
{{define "www/test.html"}}
这是模板www/test.html的内容
{{end}}因此我们可以取巧,将模板名称命名成需要建立映射关系的uri 比如外部通过http://127.0.0.1:8888/www/test.html来访问,此时req.URI.path为/www/test.html 我们可以用这个作为模板名称
如何枚举模板
这里用到了ParseGlob函数,这个函数本质上是对filepath.ParseGlob()和template.ParseFiles()的封装,可以遍历满足一定格式的路径的所有文件,假设我们建立模板存放目录internal\view如下
tree /F /A | go.mod | go.sum | html.api | html.go | readme.md | +---etc | html-api.yaml | \---internal +---config | config.go | +---handler | | routes.go | | | \---open | authorizationhandler.go | +---logic | \---open | authorizationlogic.go | +---svc | servicecontext.go | +---types | types.go | \---view +---public | footer.html | header.html | \---www index.html test.html
则我们可以使用格式字符串 ./internal/view/**/* 来遍历并解析并解析模板,建立模板和uri之间的对应关系,核心代码如下
  gloabtemplate,err:=template.New("").Funcs(FuncMap()).ParseGlob("./internal/view/**/*")
  //range轮询 
  for _, tpl := range gloabtemplate.Templates() {
		patern := tpl.Name()
		if !strings.HasPrefix(patern, "/") {
			patern = "/" + patern
		}
		//首页默认index.html index.htm index.php
		tplname := tpl.Name()
		if 0 == len(tplname) {
			tplname = serverCtx.Config.TemplateIndex
		}
		pageRouters = append(pageRouters, rest.Route{
			Method:  http.MethodGet,
			Path:    patern,
			Handler: htmlhandler(gloabtemplate, tplname, serverCtx),
		})
		logx.Infof("register page %s  %s", patern, tplname)
	}
	//添加到engin路由中
	engine.AddRoutes(pageRouters)如何在模板中使用函数
有时候我们需要在模板中使用函数,则需要用到函数映射功能,golang提供接口函数Funcs()来注入,
假设我们需要在/www/version.html中查看系统版本,应该怎么做呢?
- 定义相关函数 
//handlers\funcs.go
package handler
import (
	"html/template"
)
//定义
var funcsMap template.FuncMap = make(template.FuncMap)
func FuncMap() template.FuncMap {
	funcsMap["version"] = version
	funcsMap["hello"] = hello
	return funcsMap
}
func version() string {
	//这个函数返回当前版本号0.0.1
	return "0.01"
}
func hello(str string) string {
	//这个函数返回当前版本号0.0.1
	return "hello "+ str
}应用可以通过 template.New("").Funcs(FuncMap())来注入响应函数
- 定义模板文件 新建文件 - view/www/version.html,内容如下
{{define "www/version.html"}}
当前版本号:{{version}}
这里测试带参数的函数:{{hello "word"}}
{{end}}- 无参数的函数展示 此时模板文件中通过 - {{version}}即可调用并显示版本号0.01
- 有参数的函数 对应有参数的函数,按照参数顺序排列,中间用空格隔开 
- 以上显示结果 
当前版本号:0.01 这里测试带参数的函数:hello word
如何模板嵌套
使用templete指令进行嵌套
新建view/public/header.html内容如下
这是Head
新建view/public/footer.html内容如下
这是footer
新建view/www/index.html文件,内容如下
{{template "header.html" .}}
 
  这是Index的内容
 
 
{{template "footer.html" .}}
此时编译后即可得到如下内容
这是Head 这是Index的内容 这是footer
如何在模板中使用变量
- 在模板中直接使用 首先需要将变量暴露到模板中,这里我们使用到了 - ExecuteTemplate函数,该函数第三个参数即可以在模板里面访问的参数,比如如下代码,则在模板中可以访问Query了
data := r.URI.Query err := t.ExecuteTemplate(w, tplname, data)
新建view/www/arg.html文件
{{define "www/arg.html"}}
arga={{.arga}}
argb={{.argb}}
{{end}}请求访问方式http://127.0.0.1:8888/www/arg.html?arga=123&argb=456
系统返回结果
arga=[123] argb=[456]
- 在嵌套模板中使用 
在嵌套模板中使用需要将对象传入,方式是在模板名后加一个.,如下 新建view/www/embd.html文件
{{define "www/embd.html"}}
没加点:{{template "www/arg.html"}}
=======
加点:{{template "www/arg.html" .}}
{{end}}结果如下
没加点:arga=
argb=
======= 加点:arga=[123]
argb=[456]
如何实现模板热更新
假设我们的应用支持开发模式和生产模式,在生产模式下,由于有性能考虑,系统不需要每次访问都解析模板。而在开发模式下,每个模板有所任何小的修改,我们都希望模板能自动更新,怎么实现这个功能呢? 方案很多,有文件监听方案,如github.com/fsnotify/fsnotify监听模板目录,也有标记位方案,无论模板有没有变动,只要是开发模式,每次请求都重新加载模板并解析,gin就是这种方案,本文也采用这种方案,核心代码如下
//模板名字就是r.URL.Path
		t := gloabtemplate
		//如果是debug模式
		if serverCtx.Config.Debug {
			//每次都重新解析
			t, _ = template.New("").Funcs(FuncMap()).ParseGlob(serverCtx.Config.TemplatePattern)
		}
		err := t.ExecuteTemplate(w, tplname, r.URL.Query())如何设置首页
本质上是指定/请求对应的模板,以及系统错误对应的模板
for _, tpl := range gloabtemplate.Templates() {
		patern := tpl.Name()
		if !strings.HasPrefix(patern, "/") {
			patern = "/" + patern
		}
		//处理首页逻辑
		tplname := tpl.Name()
		if 0 == len(tplname) {
			//模板名称为""那么就默认首页吧
			//恰好/对应的模板名称为"",
			tplname = serverCtx.Config.TemplateIndex
		}
		pageRouters = append(pageRouters, rest.Route{
			Method:  http.MethodGet,
			Path:    patern,
			Handler: htmlhandler(gloabtemplate, tplname, serverCtx),
		})
		logx.Infof("register page %s  %s", patern, tplname)
	}404等页面
目前可以实现业务逻辑层面的404定制,如httpx.Error方法可用404.html替代。 对于部分场景如访问一个不存在的url,则需要go-zero官方提供支持,并开发接口。
集成
以上操作完成后,我们得到如下项目目录,
tree /F /A | go.mod | go.sum | html.api | html.go | readme.md | +---etc | html-api.yaml | \---internal +---config | config.go | +---handler | | funcs.go | | html.go | | routes.go | | | \---open | authorizationhandler.go | +---logic | \---open | authorizationlogic.go | +---svc | servicecontext.go | +---types | types.go | \---view +---public | 404.html | footer.html | header.html | \---www arg.html embd.html func.html index.html test.html
在routes.go中添加如下代码段即可
func RegisterHandlers(engine *rest.Server, serverCtx *svc.ServiceContext) {
	engine.AddRoutes([]rest.Route{
		{
			Method:  http.MethodPost,
			Path:    "/open/authorization",
			Handler: open.AuthorizationHandler(serverCtx),
		},
	})
	//添加这个代码段
	RegisterHtmlHandlers(engine, serverCtx)
}本文代码获取
关注公众号betaidea 输入html即可获得html解析相关代码 关注公众号betaidea 输入jwt即可获得gozero集成jwt-token相关代码 关注公众号betaidea 输入gozero即可gozero入门代码
下一篇预告
目前貌似还没找到go-zero对static file支持的例子,类似gin哪样做静态资源服务貌的例子,那么明天就写一个吧。 在go-zero的路由框架下寻找解决方案。 《用go-zero 支持文件服务》
广而告之
送福利了uniapp用户福音来啦! 历经数十万用户考验,我们的客服系统终于对外提供服务了。 你还在为商城接入客服烦恼吗?只需一行代码,即可接入啦!! 只需一行代码!!!!
/*kefu.vue*/

 建站
建站
 咨询
咨询 售后
售后
 建站咨询
建站咨询