跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
Go / Golang

Go Web 开发基础:模板引擎与控制器实战

综述由AI生成Go Web 开发基础涵盖控制器注册、请求参数解析、静态资源托管及文件上传下载等核心功能。通过结构体、Map 传递数据,利用 FuncMap 扩展自定义函数,掌握嵌套模板复用技巧。示例代码修正了路径兼容性与错误处理,适合快速构建轻量级 Go Web 服务。

PgDevote发布于 2026/3/23更新于 2026/4/265 浏览
Go Web 开发基础:模板引擎与控制器实战

Go Web 开发基础:模板引擎与控制器实战

在 Go 语言中,net/http 标准库提供了非常底层的 Web 处理能力,配合 html/template 包,我们可以快速构建动态页面。今天咱们不整虚的,直接聊聊在实际项目中如何组织控制器、处理请求以及使用模板引擎。

控制器注册方式

单控制器模式

最简单的情况,一个服务只对应一个处理器。实现 http.Handler 接口即可。

package main

import (
	"fmt"
	"net/http"
)

type MyHandle struct{}

func (m *MyHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "返回的数据")
}

func main() {
	h := &MyHandle{}
	server := &http.Server{
		Addr:    ":8090",
		Handler: h,
	}
	server.ListenAndServe()
}

多控制器模式

实际开发中,不同的 URL 通常由不同的逻辑处理。Go 支持两种主要方式:多个处理器(Handler)和多个处理函数(HandlerFunc)。

1. 多个处理器

使用 http.Handle 将不同路径绑定到实现了 ServeHTTP 方法的对象上。

package main

import (
	"fmt"
	"net/http"
)

type MyHandle struct{}

type MyOtherHandle struct{}

func (m *MyHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "路径 A 的处理结果")
}

func (m *MyOtherHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "路径 ABC 的处理结果")
}

func main {
	h := &MyHandle{}
	h2 := &MyOtherHandle{}
	
	http.Handle(, h)
	http.Handle(, h2)
	
	http.ListenAndServe(, )
}
()
"/a"
"/abc"
":8090"
nil
2. 多个处理函数

更常用的方式是直接使用函数,通过 http.HandleFunc 注册。

package main

import (
	"fmt"
	"net/http"
)

func first(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "多处理函数--first")
}

func second(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "多处理函数--second")
}

func main() {
	http.HandleFunc("/f", first)
	http.HandleFunc("/s", second)
	http.ListenAndServe(":8090", nil)
}

获取请求头与参数

1. 获取请求头

请求头信息存储在 r.Header 中,它是一个 map[string][]string。

package main

import (
	"fmt"
	"net/http"
)

func now(w http.ResponseWriter, r *http.Request) {
	h := r.Header
	fmt.Println(h)
	// 获取特定头部的第一个值
	if encodings := h["Accept-Encoding"]; len(encodings) > 0 {
		fmt.Println(encodings[0])
	}
}

func main() {
	http.HandleFunc("/now", now)
	http.ListenAndServe(":9090", nil)
}

2. 获取请求参数

注意:对于表单数据或 URL 查询参数,调用 ParseForm() 是必须的,否则 r.Form 为空。

package main

import (
	"fmt"
	"net/http"
)

func now(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() // 必须解析
	fmt.Println(r.Form)
	// 获取 url_long 参数的第一个值
	if vals := r.Form["url_long"]; len(vals) > 0 {
		fmt.Println(vals[0])
	}
}

func main() {
	http.HandleFunc("/now", now)
	http.ListenAndServe(":9090", nil)
}

3. 验证表单输入

可以使用正则表达式进行简单的格式校验,比如手机号。

package main

import (
	"fmt"
	"net/http"
	"regexp"
)

func validateMobile(s string) bool {
	matched, _ := regexp.MatchString(`^(1[3-9]\d{9})$`, s)
	return matched
}

func handleForm(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	mobile := r.Form.Get("mobile")
	
	if !validateMobile(mobile) {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "手机号格式不正确")
		return
	}
	fmt.Fprintf(w, "验证通过")
}

func main() {
	http.HandleFunc("/form", handleForm)
	http.ListenAndServe(":9090", nil)
}

项目结构建议

保持清晰的目录结构有助于维护。以下是一个常见的 Go Web 项目布局:

project-name/
├── src/
│   ├── static/
│   │   ├── css/
│   │   ├── images/
│   │   └── js/
│   ├── view/
│   │   └── index.html
│   └── main.go

HTML 模版与静态资源

1. 渲染 HTML 模版

使用 html/template 包解析并执行模版文件。

package main

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

func welcome(w http.ResponseWriter, r *http.Request) {
	t, err := template.ParseFiles("view/index.html")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	t.Execute(w, nil)
}

func main() {
	http.HandleFunc("/", welcome)
	http.ListenAndServe(":9090", nil)
}

2. 托管静态资源

将静态文件放在指定目录,利用 http.FileServer 提供访问服务。

package main

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

func welcome(w http.ResponseWriter, r *http.Request) {
	t, err := template.ParseFiles("view/index.html")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	t.Execute(w, nil)
}

func main() {
	http.HandleFunc("/", welcome)
	// 映射 /static/ 到本地 static 目录
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
	
	log.Println("Server starting on :9090")
	http.ListenAndServe(":9090", nil)
}

向模版传递数据

在 HTML 中使用 {{}} 语法获取传递给 Execute 的数据。. 代表当前上下文变量(Dot)。

1. 字符串类型

func welcome(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("view/index.html")
	t.Execute(w, "smallming")
}

HTML 模版内容:

<body>
<pre>尊敬的 {{.}} 先生/女士,您已被录取。</pre>
</body>

2. 结构体类型

结构体的字段首字母必须大写才能被模版访问。

package main

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

type User struct {
	Name string
	Age  int
}

func welcome(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("view/index.html")
	t.Execute(w, User{"张三", 19})
}

func main() {
	http.HandleFunc("/", welcome)
	http.ListenAndServe(":9090", nil)
}

HTML 模版内容:

<body>
<pre>姓名:{{.Name}}<br/>年龄:{{.Age}}</pre>
</body>

3. Map 类型

适合传递非结构化数据。

package main

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

type User struct {
	Name string
	Age  int
}

func welcome(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("view/index.html")
	m := make(map[string]interface{})
	m["user"] = User{"张三", 19}
	m["money"] = 1000
	t.Execute(w, m)
}

func main() {
	http.HandleFunc("/", welcome)
	http.ListenAndServe(":9090", nil)
}

HTML 模版内容:

<body>
<pre>姓名:{{.user.Name}}<br/>工资:{{.money}}</pre>
</body>

在模版中调用函数

1. 系统函数

可以直接调用内置方法,如时间格式化。

package main

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

func welcome(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("view/index.html")
	t.Execute(w, time.Now())
}

func main() {
	http.HandleFunc("/", welcome)
	http.ListenAndServe(":9090", nil)
}

HTML 模版内容:

<body>
<pre>完整时间:{{.}}<br/>年:{{.Year}}<br/>月:{{.Month}}<br/>日:{{.Day}}<br/>格式化:{{.Format "2006-01-02 15:04:05"}}</pre>
</body>

2. 自定义函数

需要借助 template.FuncMap 进行注册。

package main

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

func MyFormat(t time.Time) string {
	return t.Format("2006-01-02 15:04:05")
}

func welcome(w http.ResponseWriter, r *http.Request) {
	fm := template.FuncMap{"mf": MyFormat}
	t := template.New("index.html").Funcs(fm)
	t, _ = t.ParseFiles("view/index.html")
	t.Execute(w, time.Now())
}

func main() {
	http.HandleFunc("/", welcome)
	http.ListenAndServe(":9090", nil)
}

HTML 模版内容:

<body>
<pre>调用格式化函数:{{mf .}}</pre>
</body>

控制流与嵌套

1. 比较运算

模版支持基本的比较运算符:eq, ne, lt, le, gt, ge。

2. If 语句

<body>
{{if .}}执行了 if{{else}}执行了 else{{end}}
{{$n:=123}}
{{if gt $n 456}}数字较大{{else}}数字较小{{end}}
</body>

3. Range 循环

用于遍历切片或 Map。

<body>
{{range .}}{{.}}<br>{{end}}
</body>

4. 模版嵌套

为了复用头部、底部等公共部分,可以使用 define 和 template 动作。

定义模版片段:

{{define "head"}}
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head>
<body>Head Content: {{.}}</body>
</html>
{{end}}

{{define "foot"}}
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head>
<body>Foot Content: {{.}}</body>
</html>
{{end}}

{{define "layout"}}
<!DOCTYPE html><html lang="en">
<head><meta charset="UTF-8"><title>Layout</title></head>
<body>
{{template "head" "Header Params"}}
中间内容
{{template "foot" "Footer Params"}}
</body>
</html>
{{end}}

Go 代码加载: 需要一次性解析所有相关文件,然后指定主模版执行。

package main

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

func welcome(w http.ResponseWriter, r *http.Request) {
	t, err := template.ParseFiles("view/layout.gohtml", "view/head.gohtml", "view/foot.gohtml")
	if err != nil {
		log.Printf("err:%v", err)
	}
	t.ExecuteTemplate(w, "layout", nil)
}

func main() {
	http.HandleFunc("/", welcome)
	http.ListenAndServe(":9090", nil)
}

文件上传与下载

1. 文件上传

ioutil 已弃用,推荐使用 io 和 os 包。注意设置 multipart/form-data。

前端表单:

<form action="/upload" method="post" enctype="multipart/form-data">
  文件名:<input type="text" name="name"/>
  文件:<input type="file" name="file"/>
  <input type="submit" value="提交"/>
</form>

后端处理:

package main

import (
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

func upload(w http.ResponseWriter, r *http.Request) {
	fileName := r.FormValue("name")
	file, fileHeader, err := r.FormFile("file")
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	defer file.Close()

	b, _ := io.ReadAll(file)
	
	// 构造保存路径,避免 Windows/Linux 路径差异问题
	savePath := filepath.Join(os.TempDir(), fileName+strings.LastIndex(fileHeader.Filename, "."))
	if idx := strings.LastIndex(fileHeader.Filename, "."); idx != -1 {
		savePath = filepath.Join(os.TempDir(), fileName+fileHeader.Filename[idx:])
	}
	
	err = os.WriteFile(savePath, b, 0777)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	
	w.Write([]byte("文件上传成功"))
}

func main() {
	http.HandleFunc("/upload", upload)
	http.ListenAndServe(":9090", nil)
}

2. 文件下载

通过设置响应头 Content-Disposition 触发浏览器下载行为。

package main

import (
	"fmt"
	"net/http"
	"os"
)

func download(w http.ResponseWriter, r *http.Request) {
	filename := r.FormValue("filename")
	f, err := os.ReadFile(filename)
	if err != nil {
		fmt.Fprintln(w, "文件下载失败", err)
		return
	}

	h := w.Header()
	h.Set("Content-Type", "application/octet-stream")
	h.Set("Content-Disposition", "attachment;filename="+filename)
	w.Write(f)
}

func main() {
	http.HandleFunc("/download", download)
	http.ListenAndServe(":9090", nil)
}

以上就是 Go Web 开发中关于控制器、模板及文件操作的核心实践。掌握这些基础,足以应对大多数轻量级 Web 服务的构建需求。

目录

  1. Go Web 开发基础:模板引擎与控制器实战
  2. 控制器注册方式
  3. 单控制器模式
  4. 多控制器模式
  5. 1. 多个处理器
  6. 2. 多个处理函数
  7. 获取请求头与参数
  8. 1. 获取请求头
  9. 2. 获取请求参数
  10. 3. 验证表单输入
  11. 项目结构建议
  12. HTML 模版与静态资源
  13. 1. 渲染 HTML 模版
  14. 2. 托管静态资源
  15. 向模版传递数据
  16. 1. 字符串类型
  17. 2. 结构体类型
  18. 3. Map 类型
  19. 在模版中调用函数
  20. 1. 系统函数
  21. 2. 自定义函数
  22. 控制流与嵌套
  23. 1. 比较运算
  24. 2. If 语句
  25. 3. Range 循环
  26. 4. 模版嵌套
  27. 文件上传与下载
  28. 1. 文件上传
  29. 2. 文件下载
  • 💰 8折买阿里云服务器限时8折了解详情
  • 💰 8折买阿里云服务器限时8折购买
  • 🦞 5分钟部署阿里云小龙虾了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Windows 下使用 WSL 快速部署 Docker 环境
  • Altera USB-Blaster 驱动安装与 FPGA 下载配置指南
  • 机器人标准 DH 与改进 DH 参数对比
  • 钉钉 Webhook 机器人发送群消息指南
  • 前端开发中 Failed to fetch 错误的原因与解决方法
  • 从零入门大模型:推荐 5 本核心学习书籍
  • Qwen3-VL 视觉模型微调实战:LLaMA-Factory 与 WEBUI 部署
  • VSCode Copilot 无法连接网络问题解决方案
  • AI 时代下真正的大模型定义、分类与发展趋势
  • Flutter 三方库 ethereum_addresses 的鸿蒙化适配指南
  • ARM64 Linux 服务器离线安装 Nginx 指南
  • Linux 开发工具:GDB 调试器使用指南
  • Google Antigravity AI 编程工具下载与安装指南
  • 基于 FastAPI 的 Web 上位机系统设计与实现
  • LLM2Json:实现 ERNIE Bot 稳定输出结构化 JSON 数据
  • HTML5 结合 AI 实现智能场景渲染技术指南
  • Qt Creator 配置 GitHub Copilot AI 辅助编程插件指南
  • OpenClaw Webhook 详解与配置指南
  • PID 算法基础:比例积分微分控制原理
  • 递归、搜索与回溯算法:深入理解记忆化搜索

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online