参数校验与全局异常处理:统一错误码体系设计
参数校验与全局异常处理统一错误码体系设计用户体验最差的接口是什么传错参数返回一个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一处。后期维护加异常类型只需要改这一个类这就是体系化设计的价值。

相关新闻

为什么你的AI培训材料学员完课率不到35%?揭秘神经科学验证的3层注意力锚定设计法

为什么你的AI培训材料学员完课率不到35%?揭秘神经科学验证的3层注意力锚定设计法

更多请点击: https://kaifayun.com 第一章:AI培训材料完课率困局的神经科学本质 当学习者在AI课程中频繁中断、跳过视频、跳转章节甚至永久弃课时,问题往往不在于内容难度或平台故障,而深植于人类大脑的信息处理机制之中。神经科…

2026/9/22 11:00:02 阅读更多 →
AI简历初筛准确率从61%跃升至89.3%的7步落地法,含可即插即用的Python评估脚本

AI简历初筛准确率从61%跃升至89.3%的7步落地法,含可即插即用的Python评估脚本

更多请点击: https://codechina.net 第一章:AI简历初筛准确率跃升的核心洞察 AI简历初筛系统近年准确率显著提升,并非单纯依赖模型参数规模扩张,而是源于多维度协同优化的底层范式转变。关键突破点集中在语义对齐精度、领域知识注…

2026/9/21 22:27:35 阅读更多 →
GBFR-Logs终极指南:如何用免费工具轻松提升你的《碧蓝幻想:Relink》游戏表现

GBFR-Logs终极指南:如何用免费工具轻松提升你的《碧蓝幻想:Relink》游戏表现

GBFR-Logs终极指南:如何用免费工具轻松提升你的《碧蓝幻想:Relink》游戏表现 【免费下载链接】gbfr-logs GBFR Logs lets you track damage statistics with a nice overlay DPS meter for Granblue Fantasy: Relink. 项目地址: https://gitcode.com/g…

2026/9/21 21:27:28 阅读更多 →

最新新闻

3个出乎意料考点,助你从入门到精通搞定面试

3个出乎意料考点,助你从入门到精通搞定面试

3个出乎意料考点,助你从入门到精通搞定面试 版本升级后 API 全变了,这是无数开发者在深夜调试时最崩溃的瞬间。你明明照着上周的文档写的代码,今天一跑全是 Deprecated 警告,甚至直接报错。这种 出乎意料…

2026/9/22 18:33:42 阅读更多 →
Win7虚拟内存怎么设置最好 手写实现脚本告别卡顿

Win7虚拟内存怎么设置最好 手写实现脚本告别卡顿

Win7虚拟内存怎么设置最好 手写实现脚本告别卡顿 装个IDE,编译个大项目,Win7直接蓝屏或者卡死在进度条?别急着重装系统,十有八九是虚拟内存没调对。很多老鸟还在手动去系统属性里拖滑块,不仅慢还容易设错。今天咱们不整虚的,直接上手…

2026/9/22 18:33:42 阅读更多 →
充分必要条件的概念速查手册:3分钟搞懂逻辑陷阱

充分必要条件的概念速查手册:3分钟搞懂逻辑陷阱

充分必要条件的概念速查手册:3分钟搞懂逻辑陷阱 面对满屏红色的报错信息,StackTrace 堆叠得让人头晕,你是否也曾在逻辑判断里迷失方向?很多开发者在调试 if-else…

2026/9/22 18:33:42 阅读更多 →
微博抢红包源码解析:3个性能陷阱让响应慢50%

微博抢红包源码解析:3个性能陷阱让响应慢50%

微博抢红包源码解析:3个性能陷阱让响应慢50% 你复制来的抢红包脚本跑不通,或者抢到的概率低得可怜?别急着怪运气,90%的问题是代码里的性能瓶颈没调对。很多教程只给代码不给原理,导致你面对高并发场景时,连 await 和…

2026/9/22 18:32:42 阅读更多 →
hgame.com实战项目源码拆解:3步搞定面试原理追问

hgame.com实战项目源码拆解:3步搞定面试原理追问

hgame.com实战项目源码拆解:3步搞定面试原理追问 面试被问原理答不上来,简历上的实战项目瞬间变成笑话。很多兄弟在写 hgame.com 相关功能时,只抄代码不读源码,导致一遇追问就卡壳。 掘金技术社区上有个高赞帖子指出,80%…

2026/9/22 18:32:42 阅读更多 →
搞定五甲万京性能瓶颈,避开这道高频面试题

搞定五甲万京性能瓶颈,避开这道高频面试题

搞定五甲万京性能瓶颈,避开这道高频面试题 刚把网上扒来的“五甲万京”高并发处理逻辑复制到项目里,一跑直接卡死?内存飙升到 90%,CPU…

2026/9/22 18:32:41 阅读更多 →

日新闻

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天 配置环境就卡半天?别怪机器慢,多半是你没选对工具链。在Java、Go或Python的项目现场, 手写实现…

2026/9/22 0:00:41 阅读更多 →
剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑 面试被问原理答不上来,是不是常态?别慌。很多开发者对着 GitHub 开源仓库里的代码发呆,看似简单实则暗藏玄机。今天这份【剑帝加点】速查手册,直接带你拆解核心实现,把面试必考的原理讲透。…

2026/9/22 0:00:41 阅读更多 →
手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优 复制来的代码跑不通不知道怎么调?别慌,这种“复制粘贴地狱”在开发圈太常见了。尤其是做 图片压缩网站…

2026/9/22 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/22 4:32:41 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/22 4:38:57 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/22 8:51:04 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/22 2:43:42 阅读更多 →