ToolkitX
知识库工具箱

Web 开发

net/http, gin 框架, 中间件

25min·进阶

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 服务器的函数是?

下一节

数据库集成

下一节