Spring MVC 请求处理(零基础详细版)

目标:掌握路由、参数校验、统一异常/响应、拦截器链路,完成一个带校验与简单鉴权的 Todo 接口。

← 返回一级入口

学习目标与前置

建议用时:60-90 分钟准备:已有 Spring Boot 项目

Step 1:定义 DTO 与统一响应

flowchart LR
  A[请求] --> B[Controller 接收 DTO]
  B --> C[参数校验 @Valid]
  C --> D[业务处理/服务层]
  D --> E[统一响应 ApiResp]
  E --> F[异常走 @ControllerAdvice 返回错误码]
import jakarta.validation.constraints.NotBlank;

record TodoReq(@NotBlank(message = "标题必填") String title, boolean done) {}

record ApiResp<T>(int code, String msg, T data) {
  static <T> ApiResp<T> ok(T data) { return new ApiResp<>(0, "ok", data); }
  static ApiResp<Void> err(int code, String msg) { return new ApiResp<>(code, msg, null); }
}

说明:使用 record 简化数据类;code=0 表示成功,其他为业务错误。

Step 2:编写 Controller

@RestController
@RequestMapping("/api/todos")
public class TodoController {
  private final List<TodoReq> todos = new CopyOnWriteArrayList<>();

  @PostMapping
  public ApiResp<TodoReq> create(@Valid @RequestBody TodoReq req) {
    todos.add(req);
    return ApiResp.ok(req);
  }

  @GetMapping
  public ApiResp<List<TodoReq>> list() {
    return ApiResp.ok(todos);
  }
}

说明:@RequestBody 解析 JSON,@Valid 触发校验。

Step 3:统一异常处理

@ControllerAdvice
public class GlobalExceptionHandler {
  @ExceptionHandler(MethodArgumentNotValidException.class)
  public ResponseEntity<ApiResp<Void>> handleValid(MethodArgumentNotValidException ex) {
    String msg = ex.getBindingResult().getFieldError().getDefaultMessage();
    return ResponseEntity.badRequest().body(ApiResp.err(400, msg));
  }
}

说明:捕获参数校验异常,返回 400 与统一格式。

Step 4:拦截器记录耗时与简单鉴权

@Configuration
public class WebConfig implements WebMvcConfigurer {
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new HandlerInterceptor() {
      private final Logger log = LoggerFactory.getLogger("ReqTime");
      @Override
      public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
        req.setAttribute("t0", System.currentTimeMillis());
        // 简单鉴权:POST 需要头 X-Token
        if ("POST".equals(req.getMethod()) && req.getHeader("X-Token") == null) {
          res.setStatus(401);
          return false;
        }
        return true;
      }
      @Override
      public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {
        long cost = System.currentTimeMillis() - (long) req.getAttribute("t0");
        log.info("{} {} cost={}ms", req.getMethod(), req.getRequestURI(), cost);
      }
    });
  }
}

说明:可扩展为统一日志或链路追踪;鉴权逻辑可后续替换为 Session/JWT。

Step 5:运行与验证

  1. 启动应用,POST /api/todos 不带 X-Token,预期 401。
  2. 带 X-Token 发送 POST,正常返回;故意缺少 title,返回 400 和错误信息。
  3. GET /api/todos,查看统一响应结构 {code,msg,data}
  4. 查看控制台日志的耗时信息。

常见问题与排查

课堂练习

课后巩固