跳到主要内容
Go / Golang
Go Web 开发基础:模板引擎与控制器实战 综述由AI生成 Go Web 开发基础涵盖控制器注册、请求参数解析、静态资源托管及文件上传下载等核心功能。通过结构体、Map 传递数据,利用 FuncMap 扩展自定义函数,掌握嵌套模板复用技巧。示例代码修正了路径兼容性与错误处理,适合快速构建轻量级 Go Web 服务。
PgDevote 发布于 2026/3/23 更新于 2026/4/26 5 浏览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)
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)
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" )
}
<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 )
}
<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 )
}
<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 )
}
<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 )
}
<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 循环 <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)
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 服务的构建需求。
相关免费在线工具 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