RESTful设计与参数校验详解
定位:第 03 篇,讲透 REST 设计规范、版本管理、Bean Validation 校验体系与错误响应标准化
适用版本:Spring Framework 6.x(JDK 17+)
目录
一、REST 设计规范
1.1 核心原则
| 原则 | 要点 |
|---|---|
| 资源化 | 一切皆资源,用名词标识(/orders/{id}),不用动词(避免 /getOrder) |
| 无状态 | 每个请求自带鉴权与上下文,服务端不存会话(利于水平扩展) |
| 统一接口 | 用 HTTP 方法与状态码表达语义,而非自定义动词 |
1.2 资源命名与集合
复数名词: /orders、/users
层级表达从属: /users/{id}/orders(用户的订单)
动作用子资源: /orders/{id}/refund(POST,动作即资源)
避免:/getUsers、/deleteOrder、URL 里带动词
1.3 状态码规范(必记)
| 状态码 | 语义 |
|---|---|
| 200 | 成功(有响应体) |
| 201 | 创建成功(常带 Location) |
| 204 | 成功、无内容(如删除) |
| 400 | 请求参数错误 |
| 401 | 未认证(没登录/凭证无效) |
| 403 | 已认证但无权限 |
| 404 | 资源不存在 |
| 409 | 冲突(如重复创建) |
| 422 | 语义校验失败 |
| 500 | 服务端错误 |
1.4 查询、分页、过滤
过滤: /orders?status=PAID&userId=1
分页: /orders?page=0&size=20
排序: /orders?sort=createTime,desc
二、版本管理
| 策略 | 做法 | 特点 |
|---|---|---|
| URI 版本 | /v1/orders、/v2/orders | 直观、易路由,最常用 |
| 头版本 | Accept: application/vnd.api.v2+json | 更"纯",但不直观 |
| 参数版本 | ?version=2 | 少用 |
兼容性纪律(呼应接口契约):加字段向后兼容;删字段、改类型、改语义必须升版本;旧版本保留过渡期后下线。
三、参数校验
3.1 Bean Validation 注解
public class UserCreateReq {
@NotBlank(message = "用户名不能为空")
private String name;
@Email(message = "邮箱格式错误")
private String email;
@Min(value = 0) @Max(value = 150)
private Integer age;
@Size(max = 20)
private String nickname;
}
3.2 触发与失败处理
@PostMapping
User create(@RequestBody @Valid UserCreateReq req) { ... }
@Valid/@Validated 触发校验,失败抛 MethodArgumentNotValidException,交全局异常处理转 400 + 字段级错误信息。
3.3 进阶
| 特性 | 用法 |
|---|---|
| 嵌套校验 | 对象字段上加 @Valid(校验内部对象的约束) |
| 分组校验 | @Validated(Create.class),注解上标分组 |
| 自定义约束 | 自定义注解 + ConstraintValidator(如手机号) |
| 方法级校验 | 类上 @Validated,校验方法参数(配合 AOP) |
四、错误响应标准化
4.1 ProblemDetail(RFC 7807,Boot 3 内置)
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "用户名不能为空",
"instance": "/users"
}
Boot 3 可开启 spring.mvc.problemdetails.enabled=true 让默认错误走该格式。
4.2 自定义统一错误体
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
ResponseEntity<ApiError> invalid(MethodArgumentNotValidException e) {
// 提取各字段错误 → 400 + 结构化错误体
}
@ExceptionHandler(BizException.class)
ResponseEntity<ApiError> biz(BizException e) {
return ResponseEntity.badRequest().body(ApiError.of(e.getCode(), e.getMessage()));
}
}
要点:业务错误码 + 人类可读消息 + 字段级明细,状态码与错误语义对齐;细节见 04 篇异常体系。
五、总结
- REST 规范:资源化名词复数、无状态、HTTP 方法与状态码表达语义;查询/分页/排序走查询参数。
- 状态码:201 创建、204 无内容、401 未认证、403 无权限、409 冲突、422 校验失败——与语义对齐而非全 200。
- 版本管理:URI 版本最常用;加字段兼容、破坏性变更升版本。
- 校验:Bean Validation 注解 + @Valid 触发 + 全局异常转 400;支持嵌套/分组/自定义约束。
- 错误标准化:ProblemDetail(RFC 7807)或自定义统一错误体,业务码 + 可读消息 + 字段明细。
六、常见高频面试题
1. 什么是 REST?核心设计原则有哪些?
要点:REST 是以资源为中心的架构风格。原则:资源化(用名词标识资源,如 /orders/{id});统一接口(用 HTTP 方法表达操作、状态码表达结果);无状态(请求自带鉴权,服务端不存会话,利于水平扩展);分层系统与按需代码(可选)。资源用复数名词、层级表达从属,避免动词式 URL。
2. HTTP 方法中哪些是幂等的?为什么重要?
要点:幂等指同一请求执行一次与多次效果相同。GET、PUT、DELETE 幂等;POST、PATCH 非幂等(通常)。重要性:幂等性决定能否安全重试——网络超时后,幂等请求可放心重发,非幂等重试可能重复创建/扣款。这与分布式重试纪律一致(非幂等需幂等键或禁止重试)。
3. 常见状态码的语义?401 和 403 的区别?
要点:200 成功、201 创建成功、204 成功无内容、400 参数错误、404 资源不存在、409 冲突、500 服务端错误。401 未认证——身份凭证缺失或无效(“你是谁不知道”),应引导登录;403 已认证但无权限(“知道你是谁但不能做”)。混用会导致前端无法正确处理(该跳登录还是提示无权限)。
4. Spring 里如何做参数校验?
要点:用 Bean Validation(Jakarta Validation)。入参 DTO 上标约束注解(@NotBlank/@Email/@Min/@Size 等);控制器方法参数上加 @Valid 或 @Validated 触发;校验失败抛 MethodArgumentNotValidException,用 @RestControllerAdvice 全局捕获,转成 400 + 字段级错误信息。支持嵌套校验(字段加 @Valid)、分组校验、自定义约束注解。
5. 如何自定义校验规则(如手机号)?
要点:定义注解(@Phone),用 @Constraint 关联校验器;实现 ConstraintValidator<Phone, String>,在 isValid 写校验逻辑(正则)。注解即可像内置注解一样用在字段上,随 @Valid 触发。适合内置注解无法覆盖的业务规则(手机号、证件号、自定义业务合法性),复用且集中。
6. 为什么推荐统一错误响应格式?
要点:统一错误体(状态码 + 业务错误码 + 可读消息 + 字段明细)让前端/调用方可编程地处理错误(按错误码分支、展示消息),而不是解析各异的堆栈或自由格式;也利于网关聚合与日志关联。实现用 @RestControllerAdvice 全局异常处理器把各类异常映射为统一结构;Boot 3 可直接用 ProblemDetail(RFC 7807)标准。
7. API 版本管理怎么做?
要点:常见三种——URI 版本(/v1/、/v2/,最直观易路由)、请求头版本(Accept 带版本,更纯但不直观)、参数版本(少用)。兼容纪律:加字段向后兼容,删字段/改类型/改语义必须升版本;旧版本保留过渡期并公告下线时间。目标是让演进不破坏存量客户端。
8. 校验失败应该返回什么状态码?
要点:入参格式/约束校验失败通常返回 400(Bad Request)并带字段级错误明细;语义层面的失败(格式对但业务不满足)可用 422(Unprocessable Entity)。关键是响应体包含哪个字段、什么原因,便于客户端修正。不要返回 500(那是服务端问题),也不要 200 包错误码(状态码与语义应对齐)。
9. @Valid 和 @Validated 的区别?
要点:都触发 Bean Validation。@Valid 是 Jakarta 标准注解,支持嵌套校验(在对象字段上标注校验内部对象),可用于字段/参数;@Validated 是 Spring 的扩展,支持分组校验(@Validated(Group.class)),用于类/方法/参数,类上标注还能启用方法级校验(配合 AOP)。日常:参数校验两者皆可,需要分组或方法级校验用 @Validated,需要嵌套用 @Valid。
10. 无状态对设计有什么影响?
要点:服务端不在内存/会话中保存客户端状态,每个请求自带全部所需信息(如 Token)。影响:① 水平扩展容易——请求可打到任意实例,无需会话粘滞;② 鉴权用自包含凭证(JWT)或集中会话存储(Redis);③ 需要上下文的场景显式传递(请求头/参数)。代价是每请求传凭证与可能的重复解析,用缓存缓解。

转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/weixin_49076592/article/details/167486948



