golang微服务框架中如何扩展go-mile米乐体育

golang微服务框架中如何扩展go-zero使之支持html模板解析自动化

本篇文章为大家展示了golang微服务框架中如何扩展go-zero使之支持html模板解析自动化,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

go-zero本身支持html模板解析,我们只需要添加url对应模板解hanlder,实现逻辑就可以了

但是winlion太懒了,我甚至想

  • 不写任何一个和模板相关的handler

  • 如果有新的模板,直接把模板到某个特定目录就好,不要动任何go代码

  • 在开发环境下没有缓存,修改了模板文件无需重启

需求在这里,开撸吧

在代码开始前,你可能需要阅读

金光灿灿的gorm v2 适合创业的golang微服务框架go-zero实战 如果对go-zero已经了解,直接跳过吧

创建项目

生成go.mod文件

以如下指令创建项目

mkdirhtmlcdhtmlgomodinithtml

定义html.api

本文设计api如下 |描述|格式|方法|参数|返回|是否需要鉴权| |----|----|----|----|----|----| |用户登录|/open/authorization|post|mobile:手机号,passwd:密码,code:图片验证码|id:用户id,token:用户token|否|

根据以上描述,书写api的模板文件如下

type(useroptreqstruct{mobilestring`form:"mobile"`passwdstring`form:"passwd"`codestring`form:"code,optional"`}useroptrespstruct{iduint`json:"id"`tokenstring`json:"token"`})servicehtml-api{@server(handler:authorizationhandlerfolder:open)post/open/authorization(useroptreq)returns(useroptresp)}

注意

  • 本文和html模板相关,可以不适用goctl工具

  • 但是由于使用工具可以为我们节省很多搭建框架相关的工作,所以建议使用用ctl生成

生成代码

采用如下指令生成代码

goctlapigo-apihtml.api-dir.

此时用go run html.go指令可以发现系统以及运行

html模板自动解析实现思路

模板解析需要了解如下俩个已知知识点

  • html网页输出本质上是get请求输出

  • 相对于一个项目来说,模板文件个数是有限的,因此我们可以将模板枚举出来,完成访模板名称和请求之间的映射

对于第一个,我们可以构建get路由来实现请求,以mile米乐体育首页请求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应用配置funchtmlhandler(gloabtemplate*template.template,tplnamestring,serverctx*svc.servicecontext)http.handlerfunc{returnfunc(whttp.responsewriter,r*http.request){//模板名字就是r.url.patht:=gloabtemplate//如果是调试模式,则支持热解析ifserverctx.config.debug{t,_=template.new("").funcs(funcmap()).parseglob(serverctx.config.templatepattern)}err:=t.executetemplate(w,tplname,r.url.query())iferr!=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|\---wwwindex.htmltest.html

则我们可以使用格式字符串 ./internal/view/**/* 来遍历并解析并解析模板,建立模板和uri之间的对应关系,核心代码如下

gloabtemplate,err:=template.new("").funcs(funcmap()).parseglob("./internal/view/**/*")//range轮询for_,tpl:=rangegloabtemplate.templates(){patern:=tpl.name()if!strings.hasprefix(patern,"/"){patern="/" patern}//mile米乐体育首页默认index.htmlindex.htmindex.phptplname:=tpl.name()if0==len(tplname){tplname=serverctx.config.templateindex}pagerouters=append(pagerouters,rest.route{method:http.methodget,path:patern,handler:htmlhandler(gloabtemplate,tplname,serverctx),})logx.infof("registerpage%s%s",patern,tplname)}//添加到engin路由中engine.addroutes(pagerouters)

如何在模板中使用函数

有时候我们需要在模板中使用函数,则需要用到函数映射功能,golang提供接口函数funcs()来注入,

假设我们需要在/www/version.html中查看系统版本,应该怎么做呢?

  1. 定义相关函数

//handlers\funcs.gopackagehandlerimport("html/template")//定义varfuncsmaptemplate.funcmap=make(template.funcmap)funcfuncmap()template.funcmap{funcsmap["version"]=versionfuncsmap["hello"]=helloreturnfuncsmap}funcversion()string{//这个函数返回当前版本号0.0.1return"0.01"}funchello(strstring)string{//这个函数返回当前版本号0.0.1return"hello" str}

应用可以通过 template.new("").funcs(funcmap())来注入响应函数

  1. 定义模板文件 新建文件view/www/version.html,内容如下

{{define"www/version.html"}}

当前版本号:{{version}}

这里测试带参数的函数:{{hello"word"}}

{{end}}
  1. 无参数的函数展示 此时模板文件中通过 {{version}} 即可调用并显示版本号0.01

  2. 有参数的函数 对应有参数的函数,按照参数顺序排列,中间用空格隔开

  3. 以上显示结果

当前版本号:0.01这里测试带参数的函数:helloword

如何模板嵌套

使用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

如何在模板中使用变量

data:=r.uri.queryerr:=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.patht:=gloabtemplate//如果是debug模式ifserverctx.config.debug{//每次都重新解析t,_=template.new("").funcs(funcmap()).parseglob(serverctx.config.templatepattern)}err:=t.executetemplate(w,tplname,r.url.query())

如何设置mile米乐体育首页

本质上是指定/请求对应的模板,以及系统错误对应的模板

for_,tpl:=rangegloabtemplate.templates(){patern:=tpl.name()if!strings.hasprefix(patern,"/"){patern="/" patern}//处理mile米乐体育首页逻辑tplname:=tpl.name()if0==len(tplname){//模板名称为""那么就默认mile米乐体育首页吧//恰好/对应的模板名称为"",tplname=serverctx.config.templateindex}pagerouters=append(pagerouters,rest.route{method:http.methodget,path:patern,handler:htmlhandler(gloabtemplate,tplname,serverctx),})logx.infof("registerpage%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|\---wwwarg.htmlembd.htmlfunc.htmlindex.htmltest.html

routes.go中添加如下代码段即可

funcregisterhandlers(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的路由框架下寻找mile米乐体育的解决方案。 《用go-zero 支持文件服务》

广而告之

送福利了uniapp用户福音来啦! 历经数十万用户考验,我们的客服系统终于对外提供服务了。 你还在为商城接入客服烦恼吗?只需一行代码,即可接入啦!! 只需一行代码!!!!

/*kefu.vue*/

网站地图