参数接收与校验¶
前端调接口时,参数怎么传、后端怎么接、怎么校验——这一章讲清。
三种接收参数的方式¶
// 1. @RequestParam:查询参数 GET /tasks?current=1&size=10
@RequestParam(defaultValue = "1") long current
// 2. @PathVariable:路径变量 DELETE /tasks/123
@PathVariable Long id
// 3. @RequestBody:请求体(JSON) POST /tasks { "title":"..." }
@RequestBody TaskRequest req
对标前端:就是你发请求时 URL 上的 ?xxx、路径里的 :id、body 里的 JSON,一一对应。
接收 JSON:DTO¶
前端 POST 一个 JSON,后端用一个 DTO(Data Transfer Object) 接收:
public class TaskRequest {
@NotBlank(message = "标题不能为空")
@Size(max = 100, message = "标题最长 100 字")
private String title;
private String description;
private Integer status;
}
参数校验:@Valid¶
DTO 上的 @NotBlank、@Size 是校验注解。但光标了没用,要在 Controller 参数上加 @Valid 才会触发:
@PostMapping
public Result<Task> create(@RequestBody @Valid TaskRequest req) { ... }
// ↑ 加了它,框架自动校验 req 里的 @NotBlank 等
校验不通过时,框架抛 MethodArgumentNotValidException,被全局异常处理器(第 24 章)接住,自动返回:
前端拿到的就是干净的错误提示,不用自己判断。
常用校验注解¶
| 注解 | 作用 |
|---|---|
@NotBlank |
字符串非 null 且非空 |
@NotNull |
非 null |
@Size(min,max) |
长度范围 |
@Min / @Max |
数值范围 |
@Email |
邮箱格式 |
@Pattern(regexp) |
正则匹配 |
实际代码¶
任务请求 DTO(带校验):
package com.javaglm.task.dto;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
/** 任务新增/修改请求 DTO(第 28 章)。 */
public class TaskRequest {
@NotBlank(message = "标题不能为空")
@Size(max = 100, message = "标题最长 100 字")
private String title;
private String description;
/** 0=待办 1=进行中 2=已完成,可选。 */
private Integer status;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
}
注册请求 DTO(用户名密码长度校验):
package com.javaglm.task.dto;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
/** 注册请求 DTO(第 29 章)。 */
public class RegisterRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度 3-20")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 30, message = "密码长度 6-30")
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Controller 里用 @Valid 触发校验(看 create / update 方法):
package com.javaglm.task.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.javaglm.task.common.Result;
import com.javaglm.task.dto.TaskRequest;
import com.javaglm.task.entity.Task;
import com.javaglm.task.service.TaskService;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
/**
* 任务接口(第 28 章):增删改查 + 分页。
* 注意 userId 来自 JWT 拦截器放进请求上下文的值(不是前端传的,防伪造)。
*/
@RestController
@RequestMapping("/tasks")
public class TaskController {
private final TaskService taskService;
public TaskController(TaskService taskService) {
this.taskService = taskService;
}
@GetMapping
public Result<IPage<Task>> list(
@RequestParam(defaultValue = "1") long current,
@RequestParam(defaultValue = "10") long size,
@RequestParam(required = false) Integer status,
HttpServletRequest request) {
Long userId = (Long) request.getAttribute("userId");
return Result.success(taskService.pageTasks(current, size, userId, status));
}
@PostMapping
public Result<Task> create(@RequestBody @Valid TaskRequest req,
HttpServletRequest request) {
Long userId = (Long) request.getAttribute("userId");
return Result.success(taskService.createTask(req, userId));
}
@PutMapping("/{id}")
public Result<Task> update(@PathVariable Long id,
@RequestBody @Valid TaskRequest req,
HttpServletRequest request) {
Long userId = (Long) request.getAttribute("userId");
return Result.success(taskService.updateTask(id, req, userId));
}
@DeleteMapping("/{id}")
public Result<?> delete(@PathVariable Long id, HttpServletRequest request) {
Long userId = (Long) request.getAttribute("userId");
taskService.deleteTask(id, userId);
return Result.success();
}
}
前端人对照¶
| 前端做法 | Java 做法 |
|---|---|
| 前端 form 校验(element-plus rules) | 后端 DTO 校验注解 + @Valid |
| 校验失败 toast 提示 | 返回 { code:400, message:"..." } |
| 前端校验只是体验 | 后端校验是底线(前端可绕过,后端不能省) |
前后端都要校验
前端校验是为了"用户体验",后端校验是为了"安全"。前端校验可以被绕过(直接用 Postman 发请求),所以后端校验绝不能省。
:octicons-arrow-left-16: 上一章:REST 与分层架构 | 下一章:统一响应与全局异常