GO自研微服务框架-页面渲染

页面渲染

在实际开发中,接口返回需要支持返回HTML,JSON,XML等,在HTML返回中,要支持模板

1. HTML

渲染HTML,需要明确几个元素

  1. content-type = text/html; charset=utf-8
  2. 模板Template
  3. 渲染数据

渲染页面的操作是用户来完成,所以需要在Context中提供对应的方法

package msgoimport ("log""net/http"
)type Context struct {W http.ResponseWriterR *http.Request
}
func (c *Context) HTML(status int, html string) error {//状态是200c.W.Header().Set("Content-Type", "text/html; charset=utf-8")c.W.WriteHeader(http.StatusOK)_, err := c.W.Write([]byte(html))return err
}
g.Get("/html", func(ctx *msgo.Context) {ctx.HTML(http.StatusOK, "<h1>GO自研微服务框架</h1>")
})

1.1 加入模板支持

func (c *Context) HTMLTemplate(name string, funcMap template.FuncMap, data any, fileName ...string) {t := template.New(name)t.Funcs(funcMap)t, err := t.ParseFiles(fileName...)if err != nil {log.Println(err)return}c.W.Header().Set("Content-Type", "text/html; charset=utf-8")err = t.Execute(c.W, data)if err != nil {log.Println(err)}
}func (c *Context) HTMLTemplateGlob(name string, funcMap template.FuncMap, pattern string, data any) {t := template.New(name)t.Funcs(funcMap)t, err := t.ParseGlob(pattern)if err != nil {log.Println(err)return}c.W.Header().Set("Content-Type", "text/html; charset=utf-8")err = t.Execute(c.W, data)if err != nil {log.Println(err)}
}
g.Get("/htmltemplate", func(ctx *msgo.Context) {user := &User{Name: "lisus",}err := ctx.HTMLTemplate("login.html", user, "tpl/login.html", "tpl/header.html")if err != nil {log.Println(err)}})g.Get("/htmltemplateGlob", func(ctx *msgo.Context) {user := &User{Name: "lisus",}err := ctx.HTMLTemplateGlob("login.html", user, "tpl/*.html")if err != nil {log.Println(err)}})
{{ define "header" }}
<h1>这是头部页</h1>
{{ end }}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>这是首页</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>{{ template "header" .}}<h1>这是登录页</h1><h2>用户名:{{ .Name }}</h2>
</body>
</html>

1.2 改造-提前将模板加载到内存

如果使用到模板,并不需要在访问的时候再加载,可以在启动的时候,就将所有的模板加载到内存中,这样加快访问速度

type Engine struct {*routerfuncMap    template.FuncMapHTMLRender render.HTMLRender
}
func (e *Engine) SetFuncMap(funcMap template.FuncMap) {e.funcMap = funcMap
}// LoadTemplateGlob 加载所有模板
func (e *Engine) LoadTemplateGlob(pattern string) {t := template.Must(template.New("").Funcs(e.funcMap).ParseGlob(pattern))e.SetHtmlTemplate(t)
}func (e *Engine) SetHtmlTemplate(t *template.Template) {e.HTMLRender = render.HTMLRender{Template: t}
}
type HTMLRender struct {Template *template.Template
}
func (c *Context) Template(name string, data any) error {c.W.Header().Set("Content-Type", "text/html; charset=utf-8")err := c.engine.HTMLRender.Template.ExecuteTemplate(c.W, name, data)return err
}
engine.LoadTemplate("tpl/*.html")
g.Get("/template", func(ctx *msgo.Context) {user := &User{Name: "lisus",}err := ctx.Template("login.html", user)if err != nil {log.Println(err)}
})

2. JSON

除了返回模板页面,在多数情况下,返回JSON的应用场景也非常普遍。

有了上面的经验,在处理返回json的时候,会变得比较容易。

json的content-type=application/json; charset=utf-8

func (c *Context) JSON(status int, data any) error {c.W.Header().Set("Content-Type", "application/json; charset=utf-8")c.W.WriteHeader(status)rsp, err := json.Marshal(data)if err != nil {return err}_, err = c.W.Write(rsp)if err != nil {return err}return nil
}
g.Get("/json", func(ctx *msgo.Context) {user := &User{Name: "lisus",}err := ctx.JSON(200, user)if err != nil {log.Println(err)}
})

3. XML

content-type=application/xml;charset=utf-8

func (c *Context) XML(status int, data any) error {c.W.Header().Set("Content-Type", "application/xml; charset=utf-8")c.W.WriteHeader(status)err := xml.NewEncoder(c.W).Encode(data)return err
}
g.Get("/xml", func(ctx *msgo.Context) {user := &User{Name: "lisus",Age:  20,}err := ctx.XML(200, user)if err != nil {log.Println(err)}
})

4. 文件

下载文件的需求,需要返回excel文件,word文件等等的

g.Get("/excel", func(ctx *msgo.Context) {ctx.File("tpl/test.xlsx")})
func (c *Context) File(filePath string) {http.ServeFile(c.W, c.R, filePath)
}

指定文件名字:

func isASCII(s string) bool {for i := 0; i < len(s); i++ {if s[i] > unicode.MaxASCII {return false}}return true
}
func (c *Context) FileAttachment(filepath, filename string) {if isASCII(filename) {c.W.Header().Set("Content-Disposition", `attachment; filename="`+filename+`"`)} else {c.W.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename))}http.ServeFile(c.W, c.R, filepath)
}

从文件系统获取:

g.Get("/fs", func(ctx *msgo.Context) {ctx.FileFromFS("test.xlsx", http.Dir("tpl"))})
func (c *Context) FileFromFS(filepath string, fs http.FileSystem) {defer func(old string) {c.R.URL.Path = old}(c.R.URL.Path)c.R.URL.Path = filepathhttp.FileServer(fs).ServeHTTP(c.W, c.R)
}

5. 重定向页面

在一些前后端分离开发中,我们需要进行页面的跳转,并不是去加载模板

func (c *Context) Redirect(status int, location string) {if (status < http.StatusMultipleChoices || status > http.StatusPermanentRedirect) && status != http.StatusCreated {panic(fmt.Sprintf("Cannot redirect with status code %d", status))}http.Redirect(c.W, c.R, location, status)
}
g.Get("/redirect", func(ctx *msgo.Context) {ctx.Redirect(http.StatusFound, "/user/template")
})

6. String

func StringToBytes(s string) []byte {return *(*[]byte)(unsafe.Pointer(&struct {stringCap int}{s, len(s)},))
}
func (c *Context) String(status int, format string, values ...any) (err error) {plainContentType := "text/plain; charset=utf-8"c.W.Header().Set("Content-Type", plainContentType)c.W.WriteHeader(status)if len(values) > 0 {_, err = fmt.Fprintf(c.W, format, values...)return}_, err = c.W.Write(StringToBytes(format))return
}
g.Get("/string", func(ctx *msgo.Context) {ctx.String(http.StatusOK, "%s 是由 %s 制作 \n", "goweb框架", "go微服务框架")})

7. 接口提取

实际上,我们需要支持的格式是很多的,将其抽象提取成接口,便于后续拓展

package renderimport "net/http"type Render interface {Render(w http.ResponseWriter) errorWriteContentType(w http.ResponseWriter)
}

internal 目录下的包,不允许被其他项目中进行导入,这是在 Go 1.4 当中引入的 feature,会在编译时执行

package renderimport ("fmt""github.com/mszlu521/msgo/internal/bytesconv""net/http"
)type String struct {Format stringData   []any
}var plainContentType = []string{"text/plain; charset=utf-8"}func (r String) WriteContentType(w http.ResponseWriter) {writeContentType(w, plainContentType)
}func (r String) Render(w http.ResponseWriter) error {return WriteString(w, r.Format, r.Data)
}func WriteString(w http.ResponseWriter, format string, data []any) (err error) {writeContentType(w, plainContentType)if len(data) > 0 {_, err = fmt.Fprintf(w, format, data...)return}_, err = w.Write(bytesconv.StringToBytes(format))return
}
func (c *Context) String(status int, format string, values ...any) (err error) {err = c.Render(status, render.String{Format: format,Data:   values,})return
}func (c *Context) Render(code int, r render.Render) error {err := r.Render(c.W)c.W.WriteHeader(code)return err
}

7.1 其他渲染方式重构

7.1.1 XML
package renderimport ("encoding/xml""net/http"
)type XML struct {Data any
}var xmlContentType = []string{"application/xml; charset=utf-8"}func (r XML) Render(w http.ResponseWriter) error {r.WriteContentType(w)return xml.NewEncoder(w).Encode(r.Data)
}func (r XML) WriteContentType(w http.ResponseWriter) {writeContentType(w, xmlContentType)
}
func (c *Context) XML(status int, data any) error {return c.Render(status, render.XML{Data: data})
}
7.1.2 JSON
package renderimport ("encoding/json""net/http"
)type JSON struct {Data any
}var jsonContentType = []string{"application/json; charset=utf-8"}func (r JSON) Render(w http.ResponseWriter) error {return WriteJSON(w, r.Data)
}
func (r JSON) WriteContentType(w http.ResponseWriter) {writeContentType(w, jsonContentType)
}func WriteJSON(w http.ResponseWriter, obj any) error {writeContentType(w, jsonContentType)jsonBytes, err := json.Marshal(obj)if err != nil {return err}_, err = w.Write(jsonBytes)return err
}
7.1.3 HTML
package renderimport ("html/template""net/http"
)type HTMLData anytype HTML struct {Template   *template.TemplateName       stringData       HTMLDataIsTemplate bool
}var htmlContentType = []string{"text/html; charset=utf-8"}type HTMLRender struct {Template *template.Template
}func (r HTML) Render(w http.ResponseWriter) error {r.WriteContentType(w)if !r.IsTemplate {_, err := w.Write([]byte(r.Data.(string)))return err}err := r.Template.ExecuteTemplate(w, r.Name, r.Data)return err
}func (r HTML) WriteContentType(w http.ResponseWriter) {writeContentType(w, htmlContentType)
}
func (c *Context) HTML(status int, html string) {c.Render(status, render.HTML{IsTemplate: false, Data: html})
}func (c *Context) HTMLTemplate(name string, data any) {c.Render(http.StatusOK, render.HTML{IsTemplate: true,Name:       name,Data:       data,Template:   c.engin.HTMLRender.Template,})
}
7.1.4 Redirect
package renderimport ("fmt""net/http"
)type Redirect struct {Code     intRequest  *http.RequestLocation string
}func (r Redirect) Render(w http.ResponseWriter) error {if (r.Code < http.StatusMultipleChoices || r.Code > http.StatusPermanentRedirect) && r.Code != http.StatusCreated {panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code))}http.Redirect(w, r.Request, r.Location, r.Code)return nil
}// WriteContentType (Redirect) don't write any ContentType.
func (r Redirect) WriteContentType(http.ResponseWriter) {}
func (c *Context) Redirect(status int, location string) {c.Render(status, render.Redirect{Code:     status,Request:  c.R,Location: location,})
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/627078.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开源内网穿透工具FRP配置SSH和网站访问,构建本地私有云NAS,非常详细的配置细节,复刻即可成功

简介&#xff1a; FRP 是一个可用于内网穿透的高性能的反向代理应用&#xff0c;支持 tcp, udp 协议&#xff0c;为 http 和 https 应用协议提供了额外的能力&#xff0c;且尝试性支持了点对点穿透。名称其实就是使用了 Fast Reverse Proxy 的首字母缩写。 需求&#xff1a; 有…

基于英特尔傲腾持久内存的下一代高性能计算存储系统DAOS

本文内容来自 Intel极限存储架构和开发首席工程师 梁震 在2021CCF全国高性能计算学术年会&#xff08;CCF HPC China 2021&#xff09;上的演讲资料&#xff0c;供大家参考。 目录 - 分布式异步对象存储(DAOS)的系统架构 - DAOS系统栈结构 - DAOS 的部署方式 - DAOS 存储服…

WebGL在家居设计领域中的应用

WebGL&#xff08;Web Graphics Library&#xff09;是一种用于在Web浏览器中进行3D图形渲染的JavaScript API。在家居设计方面&#xff0c;WebGL可以提供一些强大的应用&#xff0c;使用户能够交互式地浏览和体验设计方案。以下是一些家居设计领域中WebGL的应用&#xff0c;希…

Linux 文件搜索大师:掌握 find 命令的艺术与示例

&#x1f9d9;‍♂️ 诸位好&#xff0c;吾乃诸葛妙计&#xff0c;编程界之翘楚&#xff0c;代码之大师。算法如流水&#xff0c;逻辑如棋局。 &#x1f4dc; 吾之笔记&#xff0c;内含诸般技术之秘诀。吾欲以此笔记&#xff0c;传授编程之道&#xff0c;助汝解技术难题。 &…

括号匹配(带优先级)栈C++

在算术表达式中&#xff0c;除了加、减、乘、除等运算外&#xff0c;往往还有括号。包括有大括号 {}&#xff0c;中括号 []&#xff0c;小括号 ()&#xff0c;尖括号 <> 等。 对于每一对括号&#xff0c;必须先左边括号&#xff0c;然后右边括号&#xff1b;如果有多个括…

蓝桥杯:随意组合

题目描述&#xff1a; 算法思路&#xff1a; 主要是将其中一个数组进行全排列&#xff0c;16中排列顺序&#xff0c;再与 另外一个数组进行匹配求和。在这里就要用到next_permutation()函数&#xff0c;具体用法就是直接用数组a[]&#xff0c;进行排序next_permutation(a&…

芯品荟|电梯外呼面板屏驱市场调研报告

PART ONE 产品简介 - Introduction - 1.电梯外呼面板介绍 电梯外呼面板&#xff0c;用于显示电梯当前位置、运行状态和楼层信息&#xff0c;以便乘客在等待电梯时了解电梯的运行情况。 电梯外呼面板&#xff0c;按显示屏的种类&#xff0c;分为3类&#xff0c;分别是LED屏、L…

处理HTTP请求中的表单数据

处理HTTP请求中的表单数据是Web开发中常见的任务。在Go语言中&#xff0c;可以使用net/http包来解析HTTP请求中的表单数据。 首先&#xff0c;确保你已经创建了一个HTTP服务器&#xff0c;并且能够接收和处理POST请求。然后&#xff0c;你可以使用r.ParseForm()函数来解析请求…

Android项目架构怎么做

项目架构指南 本指南包含一些最佳做法和推荐架构&#xff0c;有助于构建强大而优质的应用。 注意&#xff1a; 本页假定您对 Android 框架有基本的了解。 移动应用用户体验 典型的 Android 应用包含多个应用组件&#xff0c;包括 Activity、Fragment、Service、内容提供程序…

自然语言处理实战项目25-T5模型和BERT模型的应用场景以及对比研究、问题解答

大家好,我是微学AI,今天给大家介绍一下自然语言处理实战项目25-T5模型和BERT模型的应用场景以及对比研究、问题解答。T5模型和BERT模型是两种常用的自然语言处理模型。T5是一种序列到序列模型,可以处理各种NLP任务,而BERT主要用于预训练语言表示。T5使用了类似于BERT的预训…

融云 CEO 董晗入选「2023 福布斯中国 · 出海全球化人物 TOP30」

近日&#xff0c;福布斯中国发布“出海全球化 30&30”评选结果&#xff0c;融云 CEO 董晗入选“2023 福布斯中国 出海全球化人物 TOP30”。移步【融云全球互联网通信云】了解更多 在全球市场新秩序的构建中&#xff0c;中国品牌的影响力和作用日益凸显。针对中国出海全球…

操作系统课程设计:常用页面置换算法(OPT、FIFO、LRU)的实现及缺页率的计算(C语言)

名人说&#xff1a;莫听穿林打叶声&#xff0c;何妨吟啸且徐行。—— 苏轼《定风波莫听穿林打叶声》 Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#xff09; 目录 一、效果图二、代码&#xff08;带注释&#xff09;三、说明 一、效果图 二、代码&#xff08;带…

git中的语法和术语含义

目录 第一章、git常用术语1.1&#xff09;文件状态1.2&#xff09;git常用术语的含义 第二章、git文件状态解析2.1&#xff09;从git init开始&#xff1a;Untracked&#xff08;未跟踪&#xff09;2.2&#xff09;git add fileName后&#xff1a;Staged&#xff08;已暂存&…

XBox提升下载速度的方法

开头语&#xff1a; 欢迎大家来到本文&#xff01;如果你是Xbox玩家&#xff0c;相信下载速度对你来说是一个不可忽视的问题。本文将分享一些提升Xbox下载速度的方法&#xff0c;帮助你更快地获取游戏和更新。让我们一起来了解这些方法吧&#xff01; 方法一&#xff1a;有线连…

超强文档搜索引擎AnyTXT Searcher本地搭建

文章目录 前言1. AnyTXT Searcher1.1 下载安装AnyTXT Searcher 2. 下载安装注册cpolar3. AnyTXT Searcher设置和操作3.1 AnyTXT结合cpolar—公网访问搜索神器3.2 公网访问测试 4. 固定连接公网地址 前言 你是否遇到过这种情况&#xff0c;异地办公或者不在公司&#xff0c;想找…

【扩散模型】有/无分类器引导

那么&#xff0c;分类器引导生成是否意味着我训练了一个生成模型和一个分类器模型&#xff0c;然后在使用贝叶斯公式进行推理时将它们连接在一起?而在无分类器生成中&#xff0c;生成是在半监督数据上进行训练的&#xff0c;即部分数据被标注了标题&#xff0c;但大部分没有&a…

探索GpuMall智算云平台的AI云计算:SSH连接GPU云主机进行深度学习

#GpuMall# #GpuMall智算云# #算力租赁# #ai# 在人工智能和机器学习的领域中&#xff0c;获取强大的计算资源已经成为推进项目进展的关键。随着AI研究的深入&#xff0c;需求对GPU加速的计算能力也在不断提升。GPU云主机、GPU云服务器、GPU闲置、GPU变现、GPU收益、AI云、算力…

Copley高性能工业伺服驱动器制造者,为客户提供运动控制方案

在当今高度自动化的工业领域中&#xff0c;驱动器作为核心部件&#xff0c;其性能和稳定性对整个系统的运行至关重要。北京北成新控伺服技术有限公司深谙此道&#xff0c;成为Copley高性能工业伺服驱动器重要合作伙伴&#xff0c;以满足市场对于高精度、高动态性能的运动控制解…

入门实战丨Python小游戏经典案例

文章目录 写在前面判断与循环小游戏猜数游戏龙的世界 写在后面 写在前面 本期内容&#xff1a;两个个简单的Python小游戏入门案例。 实验需求&#xff1a;python 实验目标&#xff1a;掌握基本的判断与循环语句。 判断与循环 判断与循环是编程中非常重要的两个概念&#x…

Java零基础教学文档servlet(3)

【AJax】 1.传统开发模式的不足 传统开发模式基于浏览器数据传输功能,页面填写数据/展示数据。浏览器通过访问一个URL地址&#xff0c;将页面的数据提交给服务器。服务器将需要展示的数据返回给浏览器&#xff0c;浏览器再进行数据解析&#xff0c;将数据呈现在用户面前。这种…