Gee教程6.模板(HTML Template)

news2024/11/27 14:51:46

这一章节的内容是介绍 Web 框架如何支持服务端渲染的场景

  • 实现静态资源服务(Static Resource)。
  • 支持HTML模板渲染。

这一章节很多内容是基于net/http库的,该库已经实现了很多静态文件和HMML模板的相关功能的了。

 静态文件

网页的三剑客,JavaScript、CSS 和 HTML。要做到服务端渲染,第一步便是要支持 JS、CSS 等静态文件。

http.FileServer

为了方便使用像JavaScript、CSS和图像这样的静态资源,net/http内置了http文件服务器http.FileServer。

func main() {
	fs := http.FileServer(http.Dir("./static"))
	http.Handle("/assets/", http.StripPrefix("/assets/", fs))
	http.ListenAndServe("localhost:10000", nil)
}

我们使用了内置的http.FileServer,并将其指向url路径。为了使文件服务器正常工作,它需要知道从哪里提供文件。第二行代码就是告知静态文件是在路径./static。http.FileServer() 方法返回的是 fileHandler 实例,而 fileHandler 结构体实现了 Handler 接口的方法 ServeHTTP()

一旦我们的文件服务器就位,我们只需要将一个url路径指向它,就像我们对动态请求所做的一样。需要注意的一点是:为了正确地提供文件,我们需要去掉url路径的一部分。通常这是我们文件所在目录的名称。这是第三行代码http.StripPrefix的操作。

比如当前静态文件是在./static路径中,该路径有文件gee.js。

而用户访问localhost:10000/assets/gee.js。这时服务器就会把/assets/gee.js变成/gee.js,那就是访问./static/gee.js。这是正确的,而不是去访问./static/assers/gee.js。

静态文件Web服务器

找到文件后,如何返回这一步,net/http库已经实现了。因此,该web 框架要做的,仅仅是解析请求的地址,映射到服务器上文件的真实地址,交给http.FileServer处理就好了。

来看看使用方式

r := gee.New()
r.Static("/assets", "/usr/static")    //"/usr/static"是静态文件的存放路径
// 或相对路径 r.Static("/assets", "./static")
r.Run(":10000")

 r.Static就相当于r.GET。用户访问localhost:10000/assets/js/gee.js,最终会返回/usr/static/js/gee.js

代码实现HTML 模板渲染

// 静态文件服务器
func (group *RouterGroup) Static(relativePath string, root string) {
	handler := group.createStaticHandler(relativePath, http.Dir(root))
	urlPath := path.Join(relativePath, "/*filepath")
	group.GET(urlPath, handler)
}

func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
	absolutePath := path.Join(group.prefix, relativePath)
	fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))

	return func(c *Context) {
		file := c.Param("filepath")
		if _, err := fs.Open(file); err != nil {
			c.Status(http.StatusNotFound)
			return
		}
		fileServer.ServeHTTP(c.Wrtier, c.Req)
	}
}

 HTML 模板渲染

 Go语言内置了text/templatehtml/template2个模板标准库,其中html/template为 HTML 提供了较为完整的支持。包括普通变量渲染、列表渲染、对象渲染等。该web框架的模板渲染直接使用了html/template提供的能力。

html/template库

这里需要简单介绍下html/template库的函数使用,该流程主要是

构建模板对象New()-->解析数据Parse()-->应用Execute()

新建一个模板

使用func(*Template) New(name string) *Template新建一个模板,并指定模板的名称。

比如:tpl := template.New("test")

文件模板解析:ParseFiles和ParseGlob

ParseFiles接受一个字符串,字符串的内容是一个模板文件的路径(绝对路径or相对路径)。
ParseGlob也差不多,是用正则的方式匹配多个文件。

假设一个目录里有a.txt b.txt c.txt的话,用ParseFiles需要写3行对应3个文件,如果有一百个文件呢?
而用ParseGlob只要写成template.ParseGlob("*.txt") 即可

模板的输出,ExecuteTemplate和Execute

模板下有多套模板,其中有一套模板是当前模板
可以使用Name的方式查看当前模板

err = tmpl.ExecuteTemplate(os.Stdout, "a.html", sweaters)  //指定模板名,这次为a.html
err = tmpl.Execute(os.Stdout, sweaters)  //模板名省略,打印的是当前模板

 添加模板函数

 模板文件中支持函数操作,我们可以使用func (t *Template) Funcs(funcMap FuncMap) *Template方法给模板添加函数。

函数Must,初始化简便

Must函数用于包装返回(*Template, error)的函数/方法调用。它会自动在有err的时候panic,无错的时候只返回其中的*Template,一般用于变量初始化。这在赋值给变量的时候非常简便。

比如:var t = template.Must(template.New("name").Parse("html"))

模板功能添加入框架

了解了上面的函数使用,之后,就可以在web框架中添加template功能。

type Engine struct {
	*RouterGroup
	router *router
	gorups []*RouterGroup

	htmlTemplates *template.Template
	funcMap       template.FuncMap
}

func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
	engine.funcMap = funcMap
}

func (engine *Engine) LoadHTMLGlob(path string) {
	engine.htmlTemplates = template.Must(template.New("").Funcs(engine.funcMap).ParseGlob(path))
}

首先为 Engine 添加了 *template.Template 和 template.FuncMap对象,前者将所有的模板加载进内存,后者是所有的自定义模板渲染函数。

另外,也给用户分别提供了设置自定义渲染函数funcMap和加载模板LoadHTMLGlob的方法。

注意:从这也看出来,要先使用SetFuncMap方法,才能使用LoadHTMLGlob

接下来,对原来的 (*Context).HTML()方法做了些小修改,使之支持根据模板文件名选择模板进行渲染。

type Context struct {
    // ...
	// engine pointer
	engine *Engine    //新添加的,为了HTMl方法中能访问到engine.htmlTemplates
}

// func (c *Context) HTML(code int, html string) {
func (c *Context) HTML(code int, name string, data any) {
	c.SetHeader("Content-Type", "text/html")
	c.Status(code)
	if err := c.engine.htmlTemplates.ExecuteTemplate(c.Wrtier, name, data); err != nil {
		c.Fail(500, err.Error())
	}

	//以前的做法
	// c.SetHeader("Content-Type", "text/html")
	// c.Status(code)
	// c.Wrtier.Write([]byte(html))
}

在Context.HTML方法中要想能使用到templates,那么就需要能访问到Engine。那我们在 Context 中添加了成员变量 engine *Engine,这样就能够通过 Context 访问 Engine 中的 HTML 模板。实例化 Context 时,还需要给 c.engine 赋值。

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    .............................
	c := newContext(w, req)
	c.midHandlers = middlewares
	c.engine = engine       //给 c.engine 赋值 
	engine.router.handle(c)
}

测试

 该文件目录结构

代码

<!-- templates/custom_func.tmpl -->
<html>
<body>
    <p>hello, {{.title}}</p>
    <p>Date: {{.now | FormatAsDate}}</p>
</body>
</html>
type student struct {
	Name string
	Age  int8
}

func FormatAsDate(t time.Time) string {
	year, month, day := t.Date()
	return fmt.Sprintf("%d-%02d-%02d", year, month, day)
    //return fmt.Sprintf("%d-%02d-%d", year, month, day)
}

func main() {
	r := gee.New()
	r.Use(gee.Logger())
    //设置自定义渲染函数funcMap,custom_func.tmpl文件中的FormatAsDate格式就是FormatAsDate函数返回的格式
	r.SetFuncMap(template.FuncMap{
		"FormatAsDate": FormatAsDate,
	})
	r.LoadHTMLGlob("templates/*")
	r.Static("/assets", "./static")

	stu1 := &student{Name: "Geektutu", Age: 20}
	stu2 := &student{Name: "Jack", Age: 22}
	r.GET("/", func(c *gee.Context) {
		c.HTML(http.StatusOK, "css.tmpl", nil)
	})
	r.GET("/students", func(c *gee.Context) {
		c.HTML(http.StatusOK, "arr.tmpl", gee.H{
			"title":  "gee",
			"stuArr": [2]*student{stu1, stu2},
		})
	})

	r.GET("/date", func(c *gee.Context) {
		c.HTML(http.StatusOK, "custom_func.tmpl", gee.H{
			"title": "gee",
			"now":   time.Date(2023, 12, 5, 0, 0, 0, 0, time.UTC),
		})
	})

	r.Run("localhost:10000")
}

完整代码:https://github.com/liwook/Go-projects/tree/main/gee-web/6-template

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

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

相关文章

CentOS增加虚拟内存 (Linux增加内存)

前言 因为囊中羞涩不敢言&#xff0c;所以内存只有2G&#xff0c;项目在运行的时候&#xff0c;占用的内存已经报表&#xff0c;所以有的时候就会出现宕机的情况发生&#xff0c;后面发现可以通过使用增加虚拟内存空间&#xff0c;来增加内存容量。 下面进入正题&#xff0c;讲…

聊聊 Jetpack Compose 原理 -- Compose 是如何将数据转换成 UI 的?

Compose 是一个声明式的 UI 框架&#xff0c;提供了很多开箱即用的组件&#xff0c;比如 Text()、Button、Image() 等等&#xff0c;Compose 会经过几个不同的阶段&#xff0c;最终渲染出 UI 界面。 此转换过程分为【三个阶段】&#xff1a; 组合&#xff1a; 显示什么布局&a…

【MATLAB源码-第93期】基于matlab的白鲸优化算法(BWO)和鲸鱼优化算法(WOA)机器人栅格路径规划对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 白鲸优化算法&#xff08;BWO&#xff09; 白鲸优化算法是受到白鲸捕食和迁徙行为启发的一种算法。其主要特点和步骤包括&#xff1a; 1. 搜索食物&#xff08;全局搜索&#xff09;&#xff1a;算法模仿白鲸寻找食物的行为。…

西安安泰——ATA-1220E宽带放大器

ATA-1220E宽带放大器简介 ATA-1220E是一款可放大交直流信号的差分通道宽带放大器。其最大输出电压 60Vp-p(30Vp)&#xff0c;最大输出电流1Ap&#xff08;>50Hz&#xff09;。电压增益数控可调&#xff0c;一键保存设置&#xff0c;提供了方便简洁的操作选择&#xff0c;可…

公有云迁移研究——AWS Route53

大纲 1 什么是Route 532 Route 53能做些什么# 3 通过DNS托管来实现分流3.1 创建DNS托管3.2 对托管创建记录对流量进行分配 4 通过流量策略来对流量进行分流4.1 创建流量策略 5 对比两者的区别6 推荐 在给客户从本地机房往AWS迁移的过程中&#xff0c;我们接到如下需求&#xff…

Leetcode.2477 到达首都的最少油耗

题目链接 Leetcode.2477 到达首都的最少油耗 rating : 2012 题目描述 给你一棵 n n n 个节点的树&#xff08;一个无向、连通、无环图&#xff09;&#xff0c;每个节点表示一个城市&#xff0c;编号从 0 0 0 到 n − 1 n - 1 n−1 &#xff0c;且恰好有 n − 1 n - 1 n−…

GNN Maximum Flow Problem (From Shusen Wang)

Maximum Flow Problem ShusenWang 图数据结构和算法课程笔记 Slides Maximum Flow Problem Description Naive Algorithm Residual Capacity - FlowLeft: Original GraphRight: Residual Graph - Bottleneck capacity 2- Iteration 2:- Find an augmenting path: s -&g…

Rust的From与Into Trait

Into的本质是调用了From Trait 的方法。 From是底层的方法&#xff0c;把From实现了&#xff0c;Into的实现&#xff0c;编译器会自动根据From Trait生成Into Trait的代码 编译器自动类型推导出Into Trait的U的类型&#xff0c;调用了U类型的From的方法&#xff0c;实现其他类…

解决HTTP错误500.19 - internal server error -内部服务器错误的终极指南

在开发和维护网络应用程序时&#xff0c;难免会遇到各种HTTP错误代码。其中&#xff0c;HTTP错误500.19 - 内部服务器错误可谓是最令人头痛的问题之一。当你的应用程序遇到这个错误时&#xff0c;它似乎就像一道墙壁&#xff0c;挡住了你前进的道路。但别担心&#xff0c;本篇技…

JDK 历史版本下载以及指定版本应用

参考&#xff1a; 官网下载JAVA的JDK11版本&#xff08;下载、安装、配置环境变量&#xff09;_java11下载-CSDN博客 Gradle&#xff1a;执行命令时指定 JDK 版本 - 微酷网 下载 打开官网地址 Java Downloads | Oracle 当前版本在这里&#xff0c;但是我们要下载历史版本 选…

Python中检查字符串是否仅包含字母的多种方法:深入探究

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 随着Python在数据处理和字符串操作方面的广泛应用&#xff0c;经常需要对字符串进行验证&#xff0c;确认其是否仅包含字母。本文将探讨Python中的多种方法来检查字符串是否只由字母组成&#xff0c;以及它们的应…

Windows下安装Git和Git小乌龟

目录 Git简介 Git安装 Git小乌龟简介 Git小乌龟安装 Git简介 Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地进行从很小到非常大的项目的版本管理。Git支持将本地仓库与远程仓库进行关联&#xff0c;实现多人协作开发。由于具有分布式版本控制、高效性、灵…

C语言之if语句在生活中的应用

目前为止我们已经把基础的if语句&#xff0c;但还要学以致用。下面我们数据用的都是整形&#xff0c;您也可以选择浮点型。 滴滴打车 今天我要出去玩&#xff0c;打了一辆出租车&#xff0c;师傅告诉我五公里内起步价10元&#xff0c;超过5公里后&#xff0c;每公里3元&#x…

代码随想录算法训练营 ---第五十五天

今天是 动态规划&#xff1a;编辑距离问题。 第一题&#xff1a; 简介&#xff1a; 动态规划五部曲&#xff1a; 1.确定dp数组的含义 dp[i][j] 表示以下标i-1为结尾的字符串s&#xff0c;和以下标j-1为结尾的字符串t&#xff0c;相同子序列的长度为dp[i][j]。 2.确定递推公…

【Maven】清理 maven 仓库

初始情况下&#xff0c;我们的本地仓库是没有任何jar包的&#xff0c;此时会从私服去下载&#xff08;如果没有配置&#xff0c;就直接从中央仓库去下载&#xff09;。 可能由于网络的原因&#xff0c;jar包下载不完全&#xff0c;这些不完整的jar包都是以lastUpdated结尾。此…

Jmeter 接口-加密信息发送(一百九十九)

方式1&#xff1a;使用函数助手 比如MD5加密方式&#xff1a; 如图&#xff0c;需要对${user}进行MD5加密 1、打开函数助手&#xff0c;找到MD5&#xff0c;输入需要加密的值 2、将${__MD5(${user},)}放到请求中 3、查看请求&#xff0c;请求成功 方式2&#xff1a;导入jar包…

Android画布Canvas绘制drawBitmap基于源Rect和目的Rect,Kotlin

Android画布Canvas绘制drawBitmap基于源Rect和目的Rect&#xff0c;Kotlin <?xml version"1.0" encoding"utf-8"?> <androidx.appcompat.widget.LinearLayoutCompat xmlns:android"http://schemas.android.com/apk/res/android"xmlns…

CTF特训日记day(4-6)

来复现一下2022QWB决赛的RDP题目 这两天腰疼去了趟医院 题目要求我们攻击XRDP程序&#xff0c;从而达到本地提权的效果。 首先观察XRDP程序的版本信息 rootRDP:/home/rdp/Desktop# xrdp-sesman -version xrdp-sesman 0.9.18The xrdp session managerCopyright (C) 2004-2020…

Python小案例:打印三角形

打印不同形状以及方向的三角形 分析 需要利用循环打印规则 代码部分 userint(input("请输入打印行数&#xff1a;"))# 打印正向直角三角形 def Triangls_01(user_input):for i in range(1,user_input):print("*"*i)# 打印倒向直角三角形 def Triangls_0…

用友U8 Cloud RegisterServlet SQL注入漏洞复现

0x01 产品简介 用友U8 Cloud是用友推出的新一代云ERP,主要聚焦成长型、创新型企业,提供企业级云ERP整体解决方案。 0x02 漏洞概述 用友U8 Cloud RegisterServlet接口处存在SQL注入漏洞,未授权的攻击者可通过此漏洞获取数据库权限,从而盗取用户数据,造成用户信息泄露。 …