:id匹配单段非斜杠路径参数(如/user/123→id="123"),*path匹配剩余全部路径(如/static/js/app.js→path="/js/app.js");二者均为第三方路由库特性,标准net/http不支持。

Go HTTP 路由里 :id 和 *path 的区别必须分清
Go 标准库 net/http 本身不支持路径参数解析,:id 这种写法只在第三方路由库(如 gorilla/mux、chi)中有效。标准 http.ServeMux 只做前缀匹配,无法提取 /user/123 中的 123。
常见错误是以为 http.HandleFunc("/user/:id", handler) 能自动解析 :id —— 实际会直接匹配字面量 "/user/:id" 这个路径,导致 404。
-
:id是命名参数:匹配单段非斜杠内容,例如/user/123→id="123" -
*path是通配参数:匹配剩余全部路径,例如/static/js/app.js→path="/js/app.js" - 多个
:param不能连续,/a/:x/:y合法,/a/:x:b非法
用 gorilla/mux 提取 :id 时别漏掉 Vars() 调用
gorilla/mux 把参数存在 http.Request 的上下文里,必须显式调用 mux.Vars(r) 才能拿到 map。直接读 r.URL.Path 拿不到解析结果。
import "github.com/gorilla/mux"
func handler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) // 必须这一步
id := vars["id"] // 类型是 string
if id == "" {
http.Error(w, "missing id", http.StatusBadRequest)
return
}
// id 是字符串,需手动转 int 等类型
userID, err := strconv.Atoi(id)
if err != nil {
http.Error(w, "invalid id", http.StatusBadRequest)
return
}
}
-
mux.Vars(r)返回map[string]string,不存在的 key 返回空字符串,不是 panic - 参数名大小写敏感:
router.HandleFunc("/user/{ID}", h).Methods("GET")对应vars["ID"],不是vars["id"] - 如果路由定义了
{id:[0-9]+}正则约束,mux会在匹配阶段过滤,Vars()拿到的一定是符合规则的值
chi 的 URLParam() 更轻量,但注意它不校验路径段是否为空
chi 不依赖上下文,直接从 *http.Request 解析,用 chi.URLParam(r, "id") 即可。但它不会像 mux 那样在路由注册时做正则预检,空段(如 /user//post)也会返回空字符串。
立即学习“go语言免费学习笔记(深入)”;
import "github.com/go-chi/chi/v5"
func handler(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id") // 不需要先调 Vars()
if id == "" {
http.Error(w, "id required", http.StatusBadRequest)
return
}
// 注意:这里 id 可能是 "/" 开头的路径(如果路由是 /files/*path)
// 用 chi.URLParam(r, "*path") 获取通配部分
}
-
chi.URLParam(r, "*path")返回的是完整匹配段,包括开头的/,比如/a/b/c→"/a/b/c" - 若路由是
POST /user/{id}/avatar,而请求是POST /user//avatar,chi仍会返回空字符串,需业务层额外判空 -
chi的中间件链中,URLParam始终可用,不依赖中间件顺序;但mux的Vars()必须在路由匹配之后调用(即必须在 handler 内)
自定义解析器要小心 URL 编码和路径遍历风险
如果不用路由库、自己切 r.URL.Path,必须先 url.PathUnescape,否则 %2F 会被当普通字符;同时要防 ../ 路径穿越。
- 用
path.Clean(r.URL.Path)归一化路径,再按/切分 - 不要直接
strings.Split(r.URL.Path, "/")——//user/123会多出空段 - 提取后对参数做白名单校验比依赖路由正则更可靠,尤其涉及文件系统操作时
- 例如用户 ID 只允许数字:
match, _ := regexp.MatchString(`^\d+$`, id),而不是只信路由定义
动态参数本质是信任边界——路由库帮你做了第一层路径结构校验,但业务逻辑仍要独立验证内容合法性。这点容易被忽略,尤其在快速原型阶段。











