RESTful 在 Gin 中需严格对齐路由结构、HTTP 方法语义、状态码和响应体:资源用复数名词(如 /users),操作由方法表达(GET/POST/PUT/DELETE),禁用动词路径;参数须强类型绑定并校验,状态码必须语义化(404/201/422/503等),路径参数须转换+DB存在性双重校验。

直接说结论:RESTful 在 Gin 里不是靠“风格自觉”,而是靠路由结构、HTTP 方法语义、状态码和响应体三者严格对齐。不按这个做,前端会踩坑,监控难对齐,后期加 OpenAPI 或网关策略也会翻车。
路由必须按资源层级组织,不能拼接动词
常见错误是写成 /getUserById、/deleteUser 这类 RPC 风格路径——Gin 能跑通,但违背 RESTful 核心原则,也破坏了 Prometheus 按 method 和 path 维度聚合指标的能力。
- 正确方式是把资源(如
users)作为路径主干,用 HTTP 方法表达操作:GET /users(列表)、POST /users(创建)、GET /users/:id(单查)、PUT /users/:id(全量更新) - 静态子路径(如
/users/me、/users/count)必须放在动态参数路径(/users/:id)之前注册,否则会被后者拦截 - 带正则约束的参数更安全:
/users/:id:[0-9]+,避免/users/123/xxx被误匹配
请求绑定必须用强类型结构体 + binding 标签
用 map[string]interface{} 接参看似灵活,实际等于放弃字段校验、IDE 补全、Swagger 自动生成,还会导致零值覆盖问题(比如前端传 {"name": ""},后端结构体字段非指针就会被清空)。
- 结构体字段名必须首字母大写,且显式加
json标签:Name string `json:"name"`,否则解码失败或为空字符串 - 必填字段加
binding:"required",邮箱加binding:"email",c.ShouldBindJSON()会自动返回422 Unprocessable Entity - 禁止在 handler 里手动调用
json.Unmarshal(),否则绕过 Gin 的错误拦截机制,400 错误不会进日志、也不触发统一错误中间件
响应状态码必须语义化,不能全用 200
前端靠状态码做重试、缓存、路由跳转。如果所有响应都用 200 + 自定义 {"code": 404},fetch().then() 永远不会进 catch,整个错误流控就失效了。
- 资源不存在必须返回
http.StatusNotFound(404),不是 200 + null - 创建成功必须用
http.StatusCreated(201),并设Locationheader:c.Header("Location", "/users/"+id) - 业务校验失败(如手机号格式错)优先用
http.StatusUnprocessableEntity(422),比 400 更准确 - 数据库连接失败应返回
http.StatusServiceUnavailable(503),500只留给未捕获 panic
路径参数提取后必须转换并校验,不能直传 DB
c.Param("id") 返回的是字符串,不做转换和校验就传给 DB 查询,轻则查不到,重则引发 panic(strconv.Atoi 遇到非数字崩溃)或 SQL 注入(尤其拼 raw query 时)。
- ID 是数字:用
strconv.ParseUint(c.Param("id"), 10, 64),并检查 error - ID 是 UUID:用
uuid.Parse(),别用正则粗筛 - 转换后的值必须再校验业务逻辑(比如 DB 中是否存在该 ID),不能假设参数一定合法
最容易被忽略的是:路径参数校验和数据库查询之间存在天然 gap——即使你用了 ParseUint,也不能代替 DB 层的 SELECT ... WHERE id = ? 存在性检查。这两步缺一不可,少一步就可能让 404 变成 500。


















