参数校验与全局异常处理:统一错误码体系设计

发布时间:2026/9/28 1:45:46

参数校验与全局异常处理:统一错误码体系设计 参数校验与全局异常处理统一错误码体系设计用户体验最差的接口是什么传错参数返回一个java.lang.NullPointerException堆栈。用户一脸懵前端一脸懵运维一脸懵。这篇把参数校验和异常处理搭成体系让错误信息清清楚楚。一、参数校验的重要性垃圾输入导致垃圾输出Garbage In, Garbage Out。不校验参数用户的手机号传个abc你也存进去年龄传个-999你也信。等数据脏了再清理成本翻十倍。参数校验的原则在数据进入业务逻辑之前把住关卡。二、JSR303校验注解JSR303Bean Validation是一套Java标准校验规范Hibernate Validator是其实现。SpringBoot自带引入spring-boot-starter-validation即可。2.1 常用校验注解注解作用适用类型NotNull不能为null任意类型NotEmpty不能为null且不能为空串字符串、集合NotBlank不能为null且必须含非空白字符字符串Size(min,max)长度范围字符串、集合Min/Max最小值/最大值数字Range(min,max)数值范围数字Pattern(regexp)正则匹配字符串Email邮箱格式字符串Past/Future必须是过去/未来日期日期2.2 DTO校验示例DatapublicclassUserDTO{NotBlank(message用户名不能为空)Size(min3,max20,message用户名长度3-20个字符)privateStringusername;NotBlank(message密码不能为空)Pattern(regexp^(?.*[a-z])(?.*[A-Z])(?.*\\d).{8,}$,message密码至少8位需包含大小写字母和数字)privateStringpassword;NotBlank(message邮箱不能为空)Email(message邮箱格式不正确)privateStringemail;NotNull(message年龄不能为空)Min(value0,message年龄不能小于0)Max(value150,message年龄不能大于150)privateIntegerage;Pattern(regexp^1[3-9]\\d{9}$,message手机号格式不正确)privateStringphone;}Controller中使用RestControllerRequestMapping(/api/user)publicclassUserController{PostMappingpublicResultVoidaddUser(RequestBodyValidatedUserDTOdto){userService.add(dto);returnResult.success(null);}}三、Validated vs Valid这两个注解都能触发校验但有关键区别对比项ValidValidated来源JSR303标准Spring扩展分组校验不支持支持嵌套校验支持加在属性上支持用在方法参数上支持支持用在类级别不支持支持方法级校验3.1 分组校验新增时不传id修改时必须传id。同一个DTO不同场景校验规则不同——这就需要分组校验。定义分组接口publicinterfaceAddGroup{}publicinterfaceUpdateGroup{}DTO标注分组DatapublicclassUserDTO{NotNull(message修改时id不能为空,groupsUpdateGroup.class)privateLongid;// 新增时不校验修改时校验NotBlank(message用户名不能为空,groups{AddGroup.class,UpdateGroup.class})privateStringusername;NotBlank(message密码不能为空,groupsAddGroup.class)privateStringpassword;// 新增时必填修改时选填}Controller指定分组PostMappingpublicResultVoidadd(RequestBodyValidated(AddGroup.class)UserDTOdto){userService.add(dto);returnResult.success(null);}PutMappingpublicResultVoidupdate(RequestBodyValidated(UpdateGroup.class)UserDTOdto){userService.update(dto);returnResult.success(null);}3.2 嵌套校验对象内部还有对象时用Valid触发嵌套校验DatapublicclassOrderDTO{NotBlank(message订单号不能为空)privateStringorderNo;NotNull(message收货地址不能为空)Valid// 触发内部对象的校验privateAddressDTOaddress;}DatapublicclassAddressDTO{NotBlank(message省不能为空)privateStringprovince;NotBlank(message市不能为空)privateStringcity;}四、统一错误码体系设计4.1 错误码设计原则好的错误码体系需要可读、可扩展、能定位问题。推荐用5位数字编码前两位标识模块编码段模块示例00000成功0000010xxx通用错误10001 参数校验失败20xxx用户模块20001 用户不存在30xxx订单模块30001 订单已取消40xxx商品模块40001 库存不足50xxx系统错误50001 数据库异常4.2 错误码枚举实现GetterAllArgsConstructorpublicenumErrorCode{SUCCESS(00000,操作成功,HttpStatus.OK),// 通用错误 10xxxPARAM_ERROR(10001,参数校验失败,HttpStatus.BAD_REQUEST),UNAUTHORIZED(10002,未登录或登录已过期,HttpStatus.UNAUTHORIZED),FORBIDDEN(10003,无权限访问,HttpStatus.FORBIDDEN),NOT_FOUND(10004,资源不存在,HttpStatus.NOT_FOUND),// 用户模块 20xxxUSER_NOT_FOUND(20001,用户不存在,HttpStatus.BAD_REQUEST),USER_PASSWORD_ERROR(20002,密码错误,HttpStatus.BAD_REQUEST),USER_EXISTS(20003,用户名已存在,HttpStatus.BAD_REQUEST),// 系统错误 50xxxSYSTEM_ERROR(50001,系统开小差了请稍后重试,HttpStatus.INTERNAL_SERVER_ERROR);privatefinalintcode;privatefinalStringmessage;privatefinalHttpStatushttpStatus;}五、自定义业务异常GetterpublicclassBusinessExceptionextendsRuntimeException{privatefinalErrorCodeerrorCode;publicBusinessException(ErrorCodeerrorCode){super(errorCode.getMessage());this.errorCodeerrorCode;}publicBusinessException(ErrorCodeerrorCode,Stringmessage){super(message);this.errorCodeerrorCode;}}业务代码中直接抛publicUserlogin(LoginDTOdto){UseruseruserMapper.findByUsername(dto.getUsername());if(usernull){thrownewBusinessException(ErrorCode.USER_NOT_FOUND);}if(!passwordEncoder.matches(dto.getPassword(),user.getPassword())){thrownewBusinessException(ErrorCode.USER_PASSWORD_ERROR);}returnuser;}六、全局异常处理6.1 RestControllerAdvice ExceptionHandler用AOP思想统一拦截异常Controller里不写try-catchSlf4jRestControllerAdvicepublicclassGlobalExceptionHandler{/** 业务异常 */ExceptionHandler(BusinessException.class)publicResultVoidhandleBusinessException(BusinessExceptione){log.warn(业务异常: {},e.getMessage());returnResult.error(e.getErrorCode().getCode(),e.getMessage());}/** 参数校验异常 - RequestBody Validated */ExceptionHandler(MethodArgumentNotValidException.class)publicResultVoidhandleValidException(MethodArgumentNotValidExceptione){Stringmessagee.getBindingResult().getFieldErrors().stream().map(error-error.getField(): error.getDefaultMessage()).collect(Collectors.joining(; ));log.warn(参数校验失败: {},message);returnResult.error(ErrorCode.PARAM_ERROR.getCode(),message);}/** 参数校验异常 - RequestParam/PathVariable */ExceptionHandler(ConstraintViolationException.class)publicResultVoidhandleConstraintViolationException(ConstraintViolationExceptione){Stringmessagee.getConstraintViolations().stream().map(violation-violation.getMessage()).collect(Collectors.joining(; ));log.warn(参数校验失败: {},message);returnResult.error(ErrorCode.PARAM_ERROR.getCode(),message);}/** 其他未捕获异常 */ExceptionHandler(Exception.class)publicResultVoidhandleException(Exceptione){log.error(系统异常,e);returnResult.error(ErrorCode.SYSTEM_ERROR.getCode(),ErrorCode.SYSTEM_ERROR.getMessage());}}6.2 异常处理优先级ExceptionHandler匹配规则精确匹配优先异常继承关系就近原则。比如BusinessException extends RuntimeException如果同时注册了两个ExceptionHandlerExceptionHandler(BusinessException.class)// 精确匹配优先ExceptionHandler(RuntimeException.class)// 兜底抛出BusinessException时走第一个抛出其他RuntimeException走第二个。6.3 常见校验异常类型异常类型触发场景MethodArgumentNotValidExceptionRequestBody Validated 校验失败ConstraintViolationExceptionRequestParam/PathVariable Validated 校验失败BindException表单参数校验失败HttpMessageNotReadableException请求体JSON格式错误要注意类级别Validated 方法参数RequestParam上加NotBlank触发的是ConstraintViolationException不是MethodArgumentNotValidException。这是最常踩的坑之一。RestControllerValidated// 类级别开启方法参数校验publicclassCommonController{GetMapping(/check)publicResultStringcheck(RequestParamNotBlank(messagename不能为空)Stringname){returnResult.success(ok);}}6.4 完整调用链路前端请求 → 参数校验(Validated触发) → 校验失败 → MethodArgumentNotValidException → GlobalExceptionHandler捕获 → 返回错误信息 → 校验通过 → Controller → Service业务逻辑 → 业务异常 → throw BusinessException → GlobalExceptionHandler捕获 → 返回错误码 → 系统异常 → throw Exception → GlobalExceptionHandler兜底 → 返回500整个链路中Controller层零try-catch代码干干净净异常处理逻辑全部收敛到GlobalExceptionHandler一处。后期维护加异常类型只需要改这一个类这就是体系化设计的价值。
延伸阅读

更多相关文章

2026/9/20 21:46:47

三步免费解锁Wand专业版:Wand-Enhancer本地增强终极指南

三步免费解锁Wand专业版:Wand-Enhancer本地增强终极指南 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer Wand-Enhancer是一款专为Wand&…

2026/9/28 1:42:08

LLM+多模态+RAG:健康管理辅助诊疗系统毕设全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/28 1:42:08

九联UNT405H刷机教程:4G U盘短接刷机完整流程与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/28 1:42:08

43-44题深度解析:从MIPS数据通路到标志位硬件实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/28 1:42:08

大屏触控芯片选型实战:尺寸、防水与抗干扰的系统工程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/28 1:42:08

ADS1299心电采集实战:寄存器配置、SPI通信与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/28 1:37:08

太原网站建设策划方案哪家好:3套技术栈避坑指南

太原网站建设策划方案哪家好:3套技术栈避坑指南 网站做好了没人访问,这大概是太原很多老板最头疼的事。花几万块请人做了个高大上的官网,结果后台看数据,每天只有几个IP,全是自己人。这时候你才反应过来,找的那家建站公司,只懂做图,不懂流量。…

2026/9/27 0:00:45

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/27 0:00:45

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/27 0:00:45

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/28 0:02:03

广州外贸网站建设推广:从零搭建全流程拆解与真实报价避坑

广州外贸网站建设推广:从零搭建全流程拆解与真实报价避坑 改个需求建站公司拖一周,后台改个文案还得再交一笔“技术维护费”。这种憋屈事儿,做外贸的朋友太熟悉了。很多老板在找广州外贸网站建设推广服务商时,光盯着首页好不好看,却忽略了从零搭建一个能…

2026/9/28 0:02:04

搞懂百度竞价推广价格,网站性能优化别掉链子

搞懂百度竞价推广价格,网站性能优化别掉链子 网站突然打不开,浏览器弹出红色警告“此网站存在安全风险”,后台一看全是乱码代码和奇怪的跳转链接。这种网站被黑挂马的绝望感,很多刚转行做网站的朋友都经历过,尤其是那些为了省几百块钱服务器费用的新手。…

2026/9/25 20:55:38

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/26 19:58:38

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/25 18:34:56

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑