01. net/http 构建 Web 服务器
Go 标准库的 net/http 包就能搭建生产级 HTTP 服务器,不需要第三方框架。http.HandleFunc 注册路由处理函数,http.ListenAndServe 启动服务器。处理函数接收 ResponseWriter 写响应和 Request 读请求。虽然简单但功能完整,很多 Go Web 框架底层都是基于 net/http。
go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path)
})
http.HandleFunc("/user", userHandler)
http.ListenAndServe(":8080", nil)
}
func userHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, `{"name": "小明", "age": 18}`)
}http.HandleFunc 的路径匹配是最长前缀匹配,/ 会匹配所有路径。
02. 路由多路复用器 ServeMux
Go 1.22 之后标准库的 ServeMux 支持了方法匹配和路径参数,之前只能用第三方路由库如 gorilla/mux 或 chi。现在直接 http.NewServeMux 就能用 GET/POST 方法限定和 {id} 路径参数。不需要额外依赖就能实现 RESTful API。
go
mux := http.NewServeMux()
// 方法限定
mux.HandleFunc("GET /users", listUsers)
mux.HandleFunc("POST /users", createUser)
mux.HandleFunc("GET /users/{id}", getUser)
mux.HandleFunc("DELETE /users/{id}", deleteUser)
// 提取路径参数
func getUser(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
fmt.Fprintf(w, "获取用户 %s", id)
}
http.ListenAndServe(":8080", mux)Go 1.22+ 的标准库 ServeMux 已经足够强大,小项目不需要引入第三方路由。
03. 中间件模式
中间件是一个接收 http.Handler 返回 http.Handler 的函数,可以在请求处理前后做通用的事情,比如日志记录、认证、跨域、限流等。多个中间件可以链式组合,像洋葱一样层层包裹。这种函数式组合的方式非常优雅。
go
// 日志中间件
func Logger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
// 认证中间件
func Auth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "未授权", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
// 链式组合
handler := Logger(Auth(mux))
http.ListenAndServe(":8080", handler)中间件模式让横切关注点(日志、认证)和业务逻辑解耦,代码清爽。
04. JSON API 和请求处理
Go Web API 的核心就是:读请求体用 json.NewDecoder 反序列化,写响应用 json.NewEncoder 序列化。w.Header().Set 设置响应头 Content-Type。请求信息在 r 结构体里:r.Method 是请求方法,r.URL.Query() 拿查询参数,r.Header 拿请求头。
go
type CreateUserReq struct {
Name string `json:"name"`
Email string `json:"email"`
}
func createUser(w http.ResponseWriter, r *http.Request) {
var req CreateUserReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "无效的请求体", http.StatusBadRequest)
return
}
defer r.Body.Close()
// 查询参数
page := r.URL.Query().Get("page")
// 返回 JSON
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{"id": "123"})
}别忘了关闭 r.Body,虽然 ServeHTTP 在函数结束后通常会自动关闭,但显示 defer 是个好习惯。
05. 静态文件服务
http.FileServer 可以一行代码搭建静态文件服务器,用于托管前端打包好的 HTML/CSS/JS 文件。如果需要把静态文件嵌入二进制,Go 1.16 的 embed 包可以把静态目录编译进二进制里,部署一个文件就能跑。http.StripPrefix 去掉 URL 前缀映射到文件系统路径。
go
// 服务当前目录的 static 文件夹
fs := http.FileServer(http.Dir("./static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
// embed 嵌入静态文件
//go:embed dist/*
var staticFiles embed.FS
func main() {
fs := http.FileServer(http.FS(staticFiles))
http.Handle("/", fs)
http.ListenAndServe(":8080", nil)
}embed 让 Go 程序可以编译成单个二进制文件,部署极其方便,拷一个文件就完事。
知识测验
第 1/4 题正确 0
Go 标准库中启动 HTTP 服务器的函数是?
下一节
下一节 数据库集成