底座开发指南
萌新必读
简介
功能列表
快速启动(后端项目)
快速启动(前端项目)
接口文档
热代码加载
迁移模式(适合新项目)
删除功能(以租户为例)
表结构变更(版本升级)
内网穿透
达梦数据库专属
后端手册
新建服务
代码生成【单表】(新增功能)
代码生成(树表)
功能权限·
数据权限
用户体系
三方登陆
OAuth 2.0(SSO 单点登录)
SaaS 多租户【字段隔离】
SaaS 多租户【数据库隔离】
WebSocket 实时通信
异常处理(错误码)
参数校验
分页实现
VO 对象转换、数据翻译
文件存储(上传下载)
Excel 导入导出
操作日志、访问日志、异常日志
MyBatis 数据库
MyBatis 联表&分页查询
多数据源(读写分离)
Redis 缓存
本地缓存
异步任务
分布式锁
幂等性(防重复提交)
请求限流(RateLimiter)
HTTP 接口签名(防篡改)
单元测试
验证码
工具类 Util
数据库文档
微服务手册
微服务调试(必读)
注册中心 Nacos
配置中心 Nacos
服务网关 Spring Cloud Gateway
服务调用 Feign
定时任务 XXL Job
消息队列(内存)
消息队列(Redis)
消息队列(RocketMQ)
消息队列(RabbitMQ)
消息队列(Kafka)
消息队列(Cloud)
分布式事务 Seata
服务保障 Sentinel
工作流手册
工作流演示
功能开启
工作流(达梦适配)
审批接入(流程表单)
审批接入(业务表单)
流程设计器(BPMN)
流程设计器(钉钉、飞书)
选择审批人、发起人自选
会签、或签、依次审批
流程发起、取消、重新发起
审批通过、不通过、驳回
审批加签、减签
审批转办、委派、抄送
执行监听器、任务监听器
流程表达式
流程审批通知
大屏手册
报表设计器
大屏设计器
支付手册
功能开启·
支付宝支付接入
微信公众号支付接入
微信小程序支付接入
支付宝、微信退款接入
会员手册
功能开启··
微信公众号登录
微信小程序登录
微信小程序订阅消息
微信小程序码
会员用户、标签、分组
会员等级、积分、签到
商城手册
商城演示
功能开启···
商城装修
在线客服
【商品】商品分类
【商品】商品属性
【商品】商品 SPU 与 SKU
【商品】商品评价
【交易】购物车
【交易】交易订单
【交易】售后退款
【交易】快递发货
【交易】门店自提
【交易】分销返佣
【营销】优惠劵
【营销】拼团活动
【营销】秒杀活动
【营销】砍价活动
【营销】满减送
【营销】限时折扣
【营销】内容管理
【统计】会员、商品、交易统计
ERP 手册
ERP 演示
【功能开启】
【产品】产品信息、分类、单位
【库存】产品库存、库存明细
【库存】其它入库、其它出库
【库存】库存调拨、库存盘点
【采购】采购订单、入库、退货
【销售】销售订单、出库、退货
【财务】采购付款、销售收款
CRM手册
CRM 演示
【功能开启】·
【线索】线索管理
【客户】客户管理、公海客户
【商机】商机管理、商机状态
【合同】合同管理、合同提醒
【回款】回款管理、回款计划
【产品】产品管理、产品分类
【通用】数据权限
【通用】跟进记录、待办事项
AI大模型手册
AI 大模型演示
功能开启-
AI 聊天对话
AI 绘画创作
AI 音乐创作
AI 写作助手
AI 思维导图
【模型接入】OpenAI
【模型接入】通义千问
【模型接入】LLAMA
【模型接入】文心一言
【模型接入】DeepSeek
【模型接入】智谱 GLM
【模型接入】讯飞星火
【模型接入】微软 OpenAI
【模型接入】谷歌 Gemini
【模型接入】Stable Diffusion
【模型接入】Midjourney
【模型接入】Suno
公众号手册
【功能开启】-
公众号接入
公众号粉丝
公众号标签
公众号消息
自动回复
公众号菜单
公众号素材
公众号图文
公众号统计
系统手册
短信配置
邮件配置
站内信配置
数据脱敏
敏感词
地区 & IP 库
运维手册
开发环境
Linux 部署
Docker 部署
Jenkins 部署
HTTPS 证书
服务监控
前端手册 Vue 3.x
开发规范
菜单路由
Icon 图标
字典数据
系统组件
通用方法
配置读取
CRUD 组件
国际化
IDE 调试
代码格式化
前端手册 Vue 2.x
开发规范·
菜单路由·
Icon 图标·
字典数据·
系统组件·
通用方法·
配置读取·
本文档使用「觅思文档专业版」发布
-
+
首页
异常处理(错误码)
## 1. 统一响应 后端提供 RESTful API 给前端时,需要响应前端 API 调用是否成功: - 如果成功,成功的数据是什么。后续,前端会将数据渲染到页面上 - 如果失败,失败的原因是什么。一般,前端会将原因弹出提示给用户 因此,需要有统一响应,而不能是每个接口定义自己的风格。一般来说,统一响应返回信息如下: - 成功时,返回成功的状态码 + 数据 - 失败时,返回失败的状态码 + 错误提示 在标准的 RESTful API 的定义,是推荐使用 HTTP 响应状态码 (opens new window)作为状态码。一般来说,我们实践很少这么去做,主要原因如下: - 业务返回的错误状态码很多,HTTP 响应状态码无法很好的映射。例如说,活动还未开始、订单已取消等等 - 学习成本高,开发者对 HTTP 响应状态码不是很了解。例如说,可能只知道 200、403、404、500 几种常见的 ### 1.1 CommonResult dtpc-cloud 项目在实践时,将状态码放在 Response Body 响应内容中返回。一共有 3 个字段,通过 CommonResult (opens new window)定义如下:  > // 成功响应 { code: 0, data: { id: 1, username: "yudaoyuanma" } } // 失败响应 { code: 233666, message: "徐妈太丑了" } 失败时的 code 字段,使用全局的错误码,稍后在 「4. 错误码」 小节来讲解。 ① 在 RESTful API 成功时,定义 Controller 对应方法的返回类型为 CommonResult,并调用 #success(T data) (opens new window)方法来返回。代码如下图:  CommonResult 的 data 字段是泛型,建议定义对应的 VO 类,而不是使用 Map 类。 ② 在 RESTful API 失败时,通过抛出 Exception 异常,具体在 「2. 异常处理」 小节。 1.2 使用 @ControllerAdvice ? 在 Spring MVC 中,可以使用 @ControllerAdvice 注解,通过 Spring AOP 拦截修改 Controller 方法的返回结果,从而实现全局的统一返回。 为什么项目不采用这种方式呢?主要原因是,这样的方式“破坏”了方法的定义,导致一些隐性的问题。例如说,Swagger 接口定义错误,展示的响应结果不是 CommonResult。 还有个原因,部分 RESTful API 不需要自动包装 CommonResult 结果。例如说,第三方支付回调只需要返回 "success" 字符串。 ## 2. 异常处理 RESTful API 发生异常时,需要拦截 Exception 异常,转换成统一响应的格式,否则前端无法处理。 ### 2.1 Spring MVC 的异常 在 Spring MVC 中,通过 @ControllerAdvice + @ExceptionHandler 注解,声明将指定类型的异常,转换成对应的 CommonResult 响应。实现的代码,可见 GlobalExceptionHandler 类,代码如下:  2.2 Filter 的异常 在请求被 Spring MVC 处理之前,是先经过 Filter 处理的,此时发生异常时,是无法通过 @ExceptionHandler 注解来处理的。只能通过 try catch 的方式来实现,代码如下:  3. 业务异常 在 Service 发生业务异常时,如果进行返回呢?例如说,用户名已经存在,商品库存不足等。常用的方案选择,主要有两种: - 方案一,使用 CommonResult 统一响应结果,里面有错误码和错误提示,然后进行 return 返回 - 方案二,使用 ServiceException 统一业务异常,里面有错误码和错误提示,然后进行 throw 抛出 选择方案一 CommonResult 会存在两个问题: 因为 Spring @Transactional 声明式事务,是基于异常进行回滚的,如果使用 CommonResult 返回,则事务回滚会非常麻烦 当调用别的方法时,如果别人返回的是 CommonResult 对象,还需要不断的进行判断,写起来挺麻烦的 因此,项目采用方案二 ServiceException 异常。 ### 3.1 ServiceException 定义 ServiceException 异常类,继承 RuntimeException 异常类(非受检),用于定义业务异常。代码如下:  3.2 ServiceExceptionUtil 在 Service 需抛出业务异常时,通过调用 ServiceExceptionUtil (opens new window)的 #exception(ErrorCode errorCode, Object... params) 方法来构建 ServiceException 异常,然后使用 throw 进行抛出。代码如下: > // ServiceExceptionUtil.java public static ServiceException exception(ErrorCode errorCode) { /** 省略参数 */ } public static ServiceException exception(ErrorCode errorCode, Object... params) { /** 省略参数 */ }  ## 4. 错误码 错误码,对应 ErrorCode (opens new window)类,枚举项目中的错误,全局唯一,方便定位是谁的错、错在哪。  ### 4.1 错误码分类 错误码分成两类:全局的系统错误码、模块的业务错误码。 ### 4.1.1 系统错误码 全局的系统错误码,使用 0-999 错误码段,和 HTTP [响应状态码](http://https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status "响应状态码")对应。虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的。 系统错误码定义在 GlobalErrorCodeConstants (opens new window)类,代码如下:  ### 4.1.2 业务错误码 模块的业务错误码,按照模块分配错误码的区间,避免模块之间的错误码冲突。 ① 业务错误码一共 10 位,分成 4 段,在 ServiceErrorCodeRange (opens new window)分配,规则与代码如下图:  ② 每个业务模块,定义自己的 ErrorCodeConstants 错误码枚举类。以 yudao-module-system 模块举例子,代码如下:  ### 4.2 错误码管理(已删除) 在管理后台的 [系统管理 -> 错误码管理] 菜单,可以进行错误码的管理。  启动中的项目会每 60 秒,加载最新的错误码配置。所以,我们在修改完错误码的提示后,无需重启项目。 ### 4.2.1 手动添加 点击 [新增] 按钮,进行错误码的手动添加。如下图所示:  ### 4.2.2 自动添加 通过dtpc.error-code.constants-class-list 配置项,设置需要自动添加的 ErrorCodeConstants 错误码枚举类。如下图所示:  项目启动时,会自动扫描对应的 ErrorCodeConstants 中的错误码,自动添加或修改错误码的配置。 注意,自动添加的错误码的类型为【自动生成】,一旦在管理后台手动 [编辑] 后,该错误码就不再支持自动修改。
何加华
2024年8月27日 09:27
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
Word文件
PDF文档
PDF文档(打印)
分享
链接
类型
密码
更新密码
有效期