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

这篇具有很好参考价值的文章主要介绍了GO自研微服务框架-页面渲染。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

页面渲染

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

1. HTML

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

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

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

package msgo

import (
	"log"
	"net/http"
)

type Context struct {
	W http.ResponseWriter
	R *http.Request
}
func (c *Context) HTML(status int, html string) error {
	//状态是200
	c.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 {
	*router
	funcMap    template.FuncMap
	HTMLRender 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 = filepath

	http.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 {
			string
			Cap 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 render

import "net/http"

type Render interface {
	Render(w http.ResponseWriter) error
	WriteContentType(w http.ResponseWriter)
}

internal 目录下的包,不允许被其他项目中进行导入,这是在 Go 1.4 当中引入的 feature,会在编译时执行文章来源地址https://www.toymoban.com/news/detail-793262.html

package render

import (
	"fmt"
	"github.com/mszlu521/msgo/internal/bytesconv"
	"net/http"
)

type String struct {
	Format string
	Data   []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 render

import (
	"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 render

import (
	"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 render

import (
	"html/template"
	"net/http"
)

type HTMLData any

type HTML struct {
	Template   *template.Template
	Name       string
	Data       HTMLData
	IsTemplate 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 render

import (
	"fmt"
	"net/http"
)

type Redirect struct {
	Code     int
	Request  *http.Request
	Location 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,
	})
}

到了这里,关于GO自研微服务框架-页面渲染的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 14-RPC-自研微服务框架

    RPC 框架是分布式领域核心组件,也是微服务的基础。 RPC (Remote Procedure Call)全称是远程过程调用,相对于本地方法调用,在同一内存空间可以直接通过方法栈实现调用,远程调用则跨了不同的服务终端,并不能直接调用。 RPC框架 要解决的就是远程方法调用的问题,并且实

    2024年03月08日
    浏览(39)
  • [golang 微服务] 7. go-micro框架介绍,go-micro脚手架,go-micro结合consul搭建微服务案例

    上一节讲解了 GRPC微服务集群 + Consul集群 + grpc-consul-resolver 相关的案例,知道了微服务之间通信采用的 通信协议 ,如何实现 服务的注册和发现 ,搭建 服务管理集群 ,以及服务与服务之间的 RPC通信方式 ,具体的内容包括: protobuf协议 , consul 及 docker部署consul集群 , GRPC框架 的

    2024年02月09日
    浏览(28)
  • Golang 打包go项目部署到linux服务器

    我们可以在终端中输入以下代码: 然后就会生成main-linux的二进制可执行文件,然后我们就可以将main-linux放到服务器中的任一目录中,然后我们就可以执行以下命令运行。 这是我们在网上可以搜索到的方法,但是我相信很多人通过这个方法尝试后发现,它并不能运行。我相信

    2024年02月16日
    浏览(44)
  • CentOS 9 x64 使用 Nginx、Supervisor 部署 Go/Golang 服务

    在 CentOS 9 x64 系统上,可以通过以下步骤来部署 Golang 服务。 安装以下软件包: Golang:Golang 编程语言 Nginx:Web 服务器 Supervisor:进程管理工具 Git:版本控制工具 EPEL:扩展软件包 可以通过以下命令来安装: 为 Git 生成 SSH 密钥,以便于进行代码管理。可以通过以下命令来生成

    2024年02月12日
    浏览(43)
  • Golang 通过开源库 go-redis 操作 NoSQL 缓存服务器

    前置条件: 1、导入库: import ( \\\"github.com/go-redis/redis/v8\\\" ) 2、搭建哨兵模式集群 具体可以百度、谷歌搜索,网上现成配置教程太多了,不行还可以搜教程视频,跟着视频博主一步一个慢动作,慢慢整。 本文只介绍通过 “主从架构 / 哨兵模式” 访问的形式,这是因为,单个

    2024年01月23日
    浏览(37)
  • Go For Web:一篇文章带你用 Go 搭建一个最简单的 Web 服务、了解 Golang 运行 web 的原理

    本文作为解决如何通过 Golang 来编写 Web 应用这个问题的前瞻,对 Golang 中的 Web 基础部分进行一个简单的介绍。目前 Go 拥有成熟的 Http 处理包,所以我们去编写一个做任何事情的动态 Web 程序应该是很轻松的,接下来我们就去学习了解一些关于 Web 的相关基础,了解一些概念,

    2023年04月14日
    浏览(41)
  • Go 语言体系下的微服务框架选型: Dubbo-go

      一、Go 微服务体系发展与选型 随着微服务技术的快速发展,其在各个领域都形成了一系列事实标准,在 Kubernetes 和容器技术加持下,云原生微服务已经成为了主流解决方案。而 Go 语言作为云原生领域最受欢迎的开发语言,正被越来越多的企业作为微服务开发的首选语言,

    2023年04月10日
    浏览(29)
  • Go 语言体系下的微服务框架选型:Dubbo-go

    随着微服务技术的快速发展,其在各个领域都形成了一系列事实标准,在 Kubernetes 和容器技术加持下,云原生微服务已经成为了主流解决方案。而 Go 语言作为云原生领域最受欢迎的开发语言,正被越来越多的企业作为微服务开发的首选语言,其中比较流行的包括 Go-micro、Go

    2024年01月21日
    浏览(31)
  • Go微服务框架及基础平台选择

    是否满足公司业务需求 维护状态 系统功能完整性 星标数 文档的完整性 安全性 项目简介 :Istio是由Google、IBM和Lyft开源的微服务管理、保护和监控框架。使用istio可以很简单的创建具有负载均衡、服务间认证、监控等功能的服务网络,而不需要对服务的代码进行任何修改。

    2024年02月04日
    浏览(26)
  • go kratos 微服务框架(笔记一)

    1.简介 B站基于Golang实现的一个轻量级开源的面向微服务的框架. Kratos框架不限制您使用任何第三方库来进行项目开发,因此您可以根据喜好来选择库进行集成。我们也会逐步针对更多被广泛使用的第三方库开发插件。 2.官方文档 https://go-kratos.dev/docs/ 3.架构图 特性: APIs:协议

    2024年02月06日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包