go-zero本身支持html模板解析,我们只需要添加url对应模板解hanlder,实现逻辑就可以了
需求在这里,开撸吧
金光灿灿的Gorm V2+适合创业的golang微服务框架go-zero实战
如果对go-zero已经了解,直接跳过吧
以如下指令创建项目
mkdir html
cd html
go mod init html
本文设计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)
}
注意
采用如下指令生成代码
goctl api go -api html.api -dir .
此时用go run html.go
指令可以发现系统以及运行
模板解析需要了解如下俩个已知知识点
对于第一个,我们可以构建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)
}
}
}
这里有几个点需要强调:
view/www/
下新建test.html
文件,即使里面没有内容,系统也会将其解析得到一个名叫test.html
的模板。www/test.html
的模板,则系统又会解析得到一个名叫www/test.html
的模板,此时存在俩个模板,一个名叫test.html
,一个名叫www/test.html
view/www/test.html
文件内容如下
{
{define "www/test.html"}}
<h1>这是模板www/test.html的内容h1>
{
{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"}}
<h1>当前版本号:{
{version}}h1>
<h1>这里测试带参数的函数:{
{hello "word"}}h1>
{
{end}}
无参数的函数展示
此时模板文件中通过 { {version}}
即可调用并显示版本号0.01
有参数的函数
对应有参数的函数,按照参数顺序排列,中间用空格隔开
以上显示结果
当前版本号:0.01
这里测试带参数的函数:hello word
使用templete
指令进行嵌套
新建view/public/header.html
内容如下
<div class="top-menu-wrapper index-menu">
<h1>这是Headh1>
div>
新建view/public/footer.html
内容如下
<div class="top-menu-wrapper index-menu">
<h1>这是footerh1>
div>
新建view/www/index.html
文件,内容如下
<html>
<head>head>
<body>
{
{template "header.html" .}}
<div class="content-box" data-spy="scroll" data-target=".section-scrollspy">
<h1>这是Index的内容h1>
div>
{
{template "footer.html" .}}
body>
html>
此时编译后即可得到如下内容
这是Head
这是Index的内容
这是footer
ExecuteTemplate
函数,该函数第三个参数即可以在模板里面访问的参数,比如如下代码,则在模板中可以访问Query了 data := r.URI.Query
err := t.ExecuteTemplate(w, tplname, data)
新建view/www/arg.html
文件
{
{define "www/arg.html"}}
<h5>arga={
{.arga}}h5>
<h5>argb={
{.argb}}h5>
{
{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}}
结果如下
没加点:
<h5>arga=</h5>
<h5>argb=</h5>
=======
加点:
<h5>arga=[123]</h5>
<h5>argb=[456]</h5>
假设我们的应用支持开发模式和生产模式,在生产模式下,由于有性能考虑,系统不需要每次访问都解析模板。而在开发模式下,每个模板有所任何小的修改,我们都希望模板能自动更新,怎么实现这个功能呢?
方案很多,有文件监听方案,如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定制,如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*/
<template>
<view>
<IdeaKefu :siteid="siteId" >IdeaKefu>
view>
template>
<script>
import IdeaKefu from "@/components/idea-kefu/idea-kefu.vue"
export default {
components:{
IdeaKefu
},
data() {
return {
siteId:2
}
}
}
开发文档地址
http://kefu.techidea8.com/html/wiki/