Go 的 net/http 包轻量可控但裸用易导致维护难、错误处理不统一、缺乏中间件和参数化路由;推荐新项目直接使用 chi 等第三方路由器,并手动控制 RESTful 方法、状态码、JSON 序列化及中间件链。
Go 的 net/http 包足够轻量且可控,适合构建生产级 RESTful 接口,但直接裸用容易写出难以维护、缺乏统一错误处理和中间件能力的代码。
http.ServeMux 做基础路由时,路径匹配不支持参数提取默认的 http.ServeMux 只支持前缀匹配(如 /api/users/),无法像 /api/users/{id} 这样提取路径段。硬编码拼接或手动 strings.Split(r.URL.Path, "/") 容易出错,且无法区分 /api/users 和 /api/users/123 的语义。
gorilla/mux 或 chi)是更稳妥的选择chi 的 chi.Router 支持 r.Get("/users/{id}", handler),且内置中间件链,推荐新项目直接使用HTTP 方法(GET/POST/PUT/DELETE)和响应状态码(200/201/404/400)全靠开发者在 http.HandlerFunc 中手动设置,没有框架层的语义约束。
r.Method,未支持的方法返回 http.StatusMethodNotAllowed
http.StatusCreated(201),并设置 Location header 指向新资源地址{"error": "user not found"}),避免混用字符串、nil 或 panicfunc createUser(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
// ... 解析 JSON
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{"id": "123"})
}
null 或空数组Go 结构体字段若为指针或带 omitempty 标签,在值为零值(0、""、nil)时会被 json.Marshal 跳过,导致前端无法区分“字段不存在”和“字段为零值”

omitempty,改用指针类型(如 *string)表示可选字段omitempty:前端常需明确知道 is_active: false 是有意设置,而非缺失omitempty),一个用于响应(无 omitempty)net/http 的中间件本质是嵌套的 http.Handler,需显式包装,比如日志、鉴权、CORS 都得一层层套进去,写法冗长且易漏掉 next.ServeHTTP。
func logging(next http.Handler) http.Handler
chi 时可直接 r.Use(middleware.Logger),内部已实现链式调度func authMiddleware(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, "unauthorized", http.StatusUnauthorized)
return
}
// ... 验证 token
next.ServeHTTP(w, r)
})
}
裸用 net/http 的最大成本不在写接口,而在补全错误处理、参数校验、日志、超时、重试这些非功能性需求;一旦项目规模超过 5 个 endpoint,就该考虑 chi 或 gin 这类轻量框架——它们不*你,但省下大量胶水代码。