当前位置: 首页 > news >正文

SpringBoot注解参数校验,给代码穿上“防弹衣”

大家好,我是小悟。

一、参数校验:程序员的“防杠精神器”

假如你的API像个热情的饭店服务员,用户说“随便来点吃的”,你就真给他上了盘空气——这可不妙!参数校验就像是那个会耐心问“要辣的还是不辣的?要牛肉还是鸡肉?”的细心服务员,确保不闹出“我要咖啡你却给我上了杯洗脚水”的尴尬。

SpringBoot的注解校验就像给你的方法参数请了个私人保镖,专门拦截那些不靠谱的输入。没有它?用户传个null过来,你的程序可能就会表演“当场崩溃”的绝活。

二、详细步骤:给代码戴上“紧箍咒”

第1步:先来点“开胃菜”——添加依赖

<!-- pom.xml里加入这个,就像泡面加卤蛋,标配! --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>

第2步:创建个“相亲简历”DTO类

import javax.validation.constraints.*; import java.util.Date; import java.util.List; /** * 用户注册DTO - 比相亲网站的个人资料要求还严格 */ public class UserRegisterDTO { @NotBlank(message = "用户名不能为空,难道您是无名氏?") @Size(min = 2, max = 20, message = "用户名长度在2-20之间,太短没存在感,太长记不住") private String username; @Email(message = "邮箱格式不对,这可不是在写情书,随便写写就行") private String email; @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$", message = "密码至少8位,包含字母和数字,别再用123456了!") private String password; @Min(value = 18, message = "未满18岁?小朋友先去写作业") @Max(value = 120, message = "超过120岁?您是老神仙吧") private Integer age; @NotNull(message = "手机号必须填,不然外卖到了找谁?") private String phone; @AssertTrue(message = "必须接受协议,虽然可能没人看") private Boolean acceptedAgreement; @Future(message = "预约时间必须是未来,时光机还没发明呢") private Date appointmentTime; @Size(min = 1, max = 3, message = "最多选3个爱好,您是想成为全能超人吗?") private List<String> hobbies; // 此处省略getter和setter,但它们确实存在,我发誓! // 用Lombok的@Data也行,但今天咱们保持纯洁的Java关系 // 自定义校验注解示例 @ValidGender private String gender; }

第3步:自定义校验注解——打造专属“安检仪”

import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; /** * 性别校验注解 - 咱们思想很开放,但数据要规范 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = GenderValidator.class) public @interface ValidGender { String message() default "性别必须是男、女或保密,您这是来自火星吗?"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } /** * 性别校验器 - 严肃的判官 */ public class GenderValidator implements ConstraintValidator<ValidGender, String> { private static final Set<String> VALID_GENDERS = new HashSet<>(Arrays.asList("男", "女", "保密")); @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value == null) { return true; // 用@NotNull管非空,咱们只管格式 } return VALID_GENDERS.contains(value); } }

第4步:控制器里使用——给API装上“安检门”

import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.NotBlank; @RestController @RequestMapping("/api/users") @Validated // 这个注解让方法参数校验生效,就像给方法吃了“严格丸” public class UserController { /** * 注册用户 - 参数校验比丈母娘挑女婿还严格 */ @PostMapping("/register") public Result register(@RequestBody @Valid UserRegisterDTO userDTO) { // 如果参数校验失败,根本走不到这里 // 就像考试不及格,进不了下一轮面试 return Result.success("注册成功,恭喜通过严格审查!"); } /** * 方法参数校验 - 连路径变量都不放过 */ @GetMapping("/{id}") public Result getUser( @PathVariable @Min(value = 1, message = "ID必须大于0,您这是要找空气用户吗?") Long id, @RequestParam @NotBlank(message = "令牌不能为空,您这是想蒙混过关?") String token) { return Result.success("找到了用户ID: " + id); } /** * 分组校验 - 根据不同场景使用不同规则 * 就像上班穿正装,在家穿睡衣,场合要分清 */ @PostMapping("/update") public Result updateUser(@RequestBody @Validated(UserUpdateGroup.class) UserUpdateDTO dto) { return Result.success("更新成功"); } } // 分组接口定义 interface UserUpdateGroup {} interface UserCreateGroup {}

第5步:全局异常处理——优雅的“救火队员”

import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.validation.FieldError; /** * 全局异常处理器 - 专业收拾校验失败的烂摊子 */ @RestControllerAdvice public class GlobalExceptionHandler { /** * 处理参数校验异常 - 把技术语言翻译成人话 */ @ExceptionHandler(MethodArgumentNotValidException.class) public Result handleValidationException(MethodArgumentNotValidException ex) { // 收集所有错误信息,就像收集考试错题 Map<String, String> errors = new HashMap<>(); ex.getBindingResult().getAllErrors().forEach(error -> { String fieldName = ((FieldError) error).getField(); String errorMessage = error.getDefaultMessage(); errors.put(fieldName, errorMessage); }); return Result.error(400, "参数校验失败", errors) .setMessage("您提交的数据有点小问题,请检查后再试哦~"); } /** * 处理ConstraintViolationException - 方法参数校验失败 */ @ExceptionHandler(ConstraintViolationException.class) public Result handleConstraintViolationException(ConstraintViolationException ex) { List<String> errors = ex.getConstraintViolations().stream() .map(violation -> violation.getMessage()) .collect(Collectors.toList()); return Result.error(400, "参数不合法", errors); } } /** * 统一返回结果 - 给前端一个标准的“成绩单” */ @Data @AllArgsConstructor @NoArgsConstructor public class Result<T> { private Integer code; private String message; private T data; private Long timestamp = System.currentTimeMillis(); public static <T> Result<T> success(T data) { return new Result<>(200, "成功", data); } public static <T> Result<T> error(Integer code, String message, T data) { return new Result<>(code, message, data); } public Result<T> setMessage(String message) { this.message = message; return this; } }

第6步:进阶玩法——嵌套校验和集合校验

/** * 订单DTO - 俄罗斯套娃式的校验 */ public class OrderDTO { @NotNull(message = "订单信息不能为空") @Valid // 这个注解让嵌套校验生效,就像班主任检查每个学生的作业 private UserDTO user; @Valid // 集合也要逐个校验,一个都别想逃 private List<@Valid OrderItemDTO> items; @Valid private AddressDTO address; } /** * 地址DTO - 精确到门牌号 */ public class AddressDTO { @NotBlank(message = "省份不能空,您这是要寄到外太空?") private String province; @NotBlank(message = "城市不能空") private String city; @Size(min = 5, max = 100, message = "详细地址5-100字,说清楚点,快递员会感谢您") private String detail; }

三、测试一下:看看“保镖”工作认不认真

// 测试Controller - 专门捣乱看系统反应 @SpringBootTest @AutoConfigureMockMvc class UserControllerTest { @Autowired private MockMvc mockMvc; @Test void testRegisterWithInvalidData() throws Exception { String invalidUserJson = """ { "username": "A", // 太短了! "email": "not-an-email", // 这不是邮箱 "password": "123", // 太弱了 "age": 10, // 未成年! "phone": null, // 空值 "acceptedAgreement": false, // 不同意协议 "appointmentTime": "2020-01-01", // 过去的时间 "hobbies": ["吃饭", "睡觉", "打豆豆", "刷手机", "发呆"] // 爱好太多 } """; mockMvc.perform(MockMvcRequestBuilders.post("/api/users/register") .contentType(MediaType.APPLICATION_JSON) .content(invalidUserJson)) .andExpect(status().isBadRequest()) // 应该返回400 .andExpect(jsonPath("$.code").value(400)) .andExpect(jsonPath("$.data").exists()) // 错误详情 .andDo(print()); // 打印响应,看看“保镖”怎么怼你 } }

四、性能优化小贴士

/** * 校验配置 - 让校验既严格又高效 */ @Configuration public class ValidationConfig { @Bean public Validator validator() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); // 可以在这里配置一些自定义设置 // 比如缓存校验器,避免重复创建 return validator; } /** * 快速失败模式 - 发现一个错误就立即返回 * 就像考试发现第一题错了就交卷(不建议真人尝试) */ @Bean public Validator fastFailValidator() { return Validation.byDefaultProvider() .configure() .addProperty("hibernate.validator.fail_fast", "true") .buildValidatorFactory() .getValidator(); } }

总结:参数校验的“人生哲理”

  1. 为什么需要参数校验?
    • 防止GIGO(垃圾进,垃圾出)——输入决定输出质量
    • 安全第一:很多安全漏洞都源于不可信的输入
    • 用户体验:早发现错误,早提示用户,别让用户猜谜
  2. 注解校验的优点:
    • 声明式:像贴标签一样简单,告别一堆if-else
    • 集中管理:规则在实体类上一目了然
    • 易于维护:改注解就能改规则,不用翻业务代码
    • 丰富内置:Spring提供了几十种注解,总有一款适合你
  3. 最佳实践建议:
    • 在DTO层做校验,保持业务层纯洁
    • 错误消息要友好,说人话,别甩技术术语
    • 区分必填和非必填字段,别要求用户填宇宙
    • 复杂逻辑用自定义校验器,别硬塞到一个注解里
    • 记得处理异常,给前端统一的错误格式
  4. 总结:
    参数校验就像给你的代码请了个:
    • 门卫大爷:不合格的一律不让进
    • 语文老师:检查格式对不对,内容全不全
    • 健身教练:严格要求,不容马虎
    • 相声演员:出错时还能用幽默的方式告诉你

严谨的程序员对待输入就像猫奴对待猫主子,既要有爱,也要有规矩!你的API会因为良好的参数校验而变得更加健壮、安全、用户友好。

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。

您的一键三连,是我更新的最大动力,谢谢

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

http://www.jsqmd.com/news/268602/

相关文章:

  • 筑牢智慧职教实训底座,无人机电力巡检 AI+虚仿 创新实训室特色架构
  • 每天一个网络知识:什么是MSTP?
  • 氯离子计哪家性价比高?从上海仪电雷磁产品线看国产高性价比选择 - 品牌推荐大师1
  • 抖音团购入驻避坑指南:优选服务商合集 - 野榜数据排行
  • 预测一下,微软最终会推出一款以 Windows 为主题的 Linux 发行版
  • GESP认证C++编程真题解析 | 202312 五级
  • str与[u8]区别
  • seaweedFs集群部署
  • 基于Python的外卖配送分析与可视化系统的设计与实现
  • 2026年全屋定制品牌权威推荐榜:整体家居/定制柜类/环保定制/高端整装等源头实力厂家综合评估
  • LangBot:五分钟打造你的专属IM机器人,支持10+聊天平台!
  • 油品分析仪生产厂家,谁家的技术先进/实力强? - 品牌推荐大师
  • 实用指南:光谱共焦传感器 LTC2400/LTC4000F 对手机镜头镜片的圆角倒角厚度测量检测
  • GESP认证C++编程真题解析 | 202312 四级
  • 2026 年靠谱的一键闪测仪厂家推荐及选购指南 - 工业仪器权威说
  • 迪赛福闪测仪:高效测量与精度稳定,助力制造升级 - 工业仪器权威说
  • 我花了 2 周用 cursor 把 Couple AI 重新做了一遍:从“能用”到“值得用”
  • 32432423
  • GESP认证C++编程真题解析 | 202312 三级
  • 详细介绍:安全体检 | 服务器的终极卫士
  • 解锁NanoBananaPro的6大应用场景:表情包、商品图、总结纲要、产品logo、漫画原创、文字转图片……
  • 2025年广佛双主轴加工中心用户推荐榜单出炉,46排刀机/Y轴/数控4+4/双主轴双刀塔/刀塔车床/数控车床/排刀机双主轴品牌选哪家 - 品牌推荐师
  • 计算机毕业设计案例】基于springboot的成人小饭桌预约下单配送微信小程序(程序+文档+讲解+定制)
  • 2026年铜锌分离炉深度选型报告:谁是高效冶金与环保回收的“破局者” - 电炉老饕
  • leetcode 1895. 最大的幻方 中等
  • 你的SCI论文总被拒?中国学生最易踩的6个坑
  • Gitee:2026年企业数字化转型中的项目管理核心引擎
  • 2026阀门管件铸造厂家权威推荐榜单:硅溶胶精密铸造/不锈钢铸造/碳钢精密铸造/非标铸造件源头厂家精选。
  • Gitee:中国企业数字化转型的核心技术引擎
  • 2026年四川机电设备制造空压机/ 螺杆空压机 /无油空压机 /静音空压机 /激光空压行业竞争格局深度分析报告:聚焦“智能数控化与集成解决方案” - 2026年企业推荐榜