AI陪伴机器人统一响应与全局异常-ApiResponse三段式
06-统一响应与全局异常-ApiResponse三段式黒漂技术佬 · AI 伙伴AI-Partner「数据接口部署与二次开发」系列 06上一篇的 19 个接口返回格式全都长一个样{code:0,message:success,data:...}。这套三段式是怎么实现的报错时 HTTP 状态码和业务码怎么配合为什么业务代码里几乎看不到 try-catch这篇把 AI 伙伴的 common 包三个类拆干净——总共不到一百行代码却是整个接口层体验的压舱石。一、ApiResponse三段式统一响应// 项目源码common/ApiResponse.javaDataNoArgsConstructorAllArgsConstructorpublicclassApiResponseT{privateintcode;// 业务码0 成功非 0 失败privateStringmessage;// 提示信息privateTdata;// 业务数据publicstaticTApiResponseTok(Tdata){returnnewApiResponse(0,success,data);}publicstaticTApiResponseTok(){returnnewApiResponse(0,success,null);}publicstaticTApiResponseTfail(intcode,Stringmessage){returnnewApiResponse(code,message,null);}publicstaticTApiResponseTfail(Stringmessage){returnnewApiResponse(500,message,null);}}三个字段各司其职code给程序判断0 成功、非 0 失败message给人看错误提示或固定 “success”data装业务数据成功时装实体失败时为 null。四个静态工厂覆盖了所有出口语义静态工厂codemessagedata使用场景ok(data)0success业务数据查询/创建成功ok()0successnull无返回值的操作fail(code, msg)自定义错误信息null明确知道错误码的失败fail(msg)500错误信息null不细分码的失败单参默认 500二、code0 vs HTTP 200两派的取舍业界对成功怎么表达有两派。一派信 HTTP 语义200 就是成功404/500 各表其义无需业务码。另一派国内后端主流微信/支付宝开放平台都是是本项目的做法HTTP 状态码只表达传输层结果业务结果放进 body 的 code 字段。本项目的组合更微妙两层都在用。业务成功时 HTTP 200 code0业务异常时 HTTP 状态码也会变见下节BusinessException 返回 HTTP 400。也就是说它没有走永远 200、错误全靠 code的极端派而是让 HTTP 状态承担粗分类4xx 客户端的锅、5xx 服务器的锅业务码承担细分类。维度纯 HTTP 派纯业务码派永远 200本项目混合派网关/监控识别错误天然支持按状态码告警失效需解析 body部分支持前端统一处理要枚举各种状态码只判 code状态码粗判 code 细判中间件友好度重试/熔断好差较好取舍本身没有标准答案重要的是全项目一致——这恰恰是三段式包装最大的价值前端只需要写一次拦截器判断 code 是否为 0非 0 弹 message齐活。三、BusinessException把业务错误变成数据// 项目源码common/BusinessException.javaGetterpublicclassBusinessExceptionextendsRuntimeException{privatefinalintcode;publicBusinessException(Stringmessage){super(message);this.code500;}publicBusinessException(intcode,Stringmessage){super(message);this.codecode;}}注意它继承的是RuntimeException非受检异常——业务代码抛它不用层层声明 throws。两个构造函数语义分明单参抛我不关心错误码的通用错误code 默认 500双参抛这个错误值得一个专属码的精确错误。项目里的实际用法比如对话服务里// 项目源码ChatService 内节选thrownewBusinessException(400,大模型 API Key 未配置…);thrownewBusinessException(AI 服务暂时不可用请稍后再试);抛出之后业务代码就撒手不管了——接下来的活全是全局异常处理器的。四、GlobalExceptionHandler三类拦截全项目兜底// 项目源码common/GlobalExceptionHandler.javaSlf4jRestControllerAdvicepublicclassGlobalExceptionHandler{ExceptionHandler(BusinessException.class)publicResponseEntityApiResponseVoidhandleBusiness(BusinessExceptione){returnResponseEntity.badRequest().body(ApiResponse.fail(e.getCode(),e.getMessage()));}ExceptionHandler(MethodArgumentNotValidException.class)publicResponseEntityApiResponseVoidhandleValidation(MethodArgumentNotValidExceptione){FieldErrorfieldErrore.getBindingResult().getFieldError();StringmessagefieldErrornull?参数校验失败:fieldError.getDefaultMessage();returnResponseEntity.badRequest().body(ApiResponse.fail(400,message));}ExceptionHandler(Exception.class)publicResponseEntityApiResponseVoidhandleOther(Exceptione){log.error(系统异常,e);returnResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.fail(500,系统繁忙请稍后再试));}}RestControllerAdvice让它成为所有 Controller 的常驻保安三类拦截各有讲究**第一类BusinessException → HTTP 400 业务码透传。**业务员主动抛的错设备不存在、提醒创建失败code 从异常里取出来原样进 body。注意 HTTP 状态固定给 400——这类错误的共同点是请求本身有问题客户端改改参数就能重试。第二类MethodArgumentNotValidException → HTTP 400 code 400。DTO 上NotNull/NotBlank校验失败时由Valid触发。消息取首个FieldError 的 defaultMessage比如userId 不能为空一个字段一个字段地修体验友好一个 FieldError 都没有就退回兜底文案参数校验失败。**第三类Exception 兆底 → HTTP 500 固定文案。**任何没被预料到的异常统一返回系统繁忙请稍后再试同时log.error(系统异常, e)把完整堆栈打进日志。这里有个教科书级的细节对外文案模糊、对内日志详尽。错误堆栈里的类名、SQL 片段绝不能透给前端信息泄露风险但日志里必须留全否则排查问题两眼一抹黑。五、为什么参数校验错误要用 400有同学会问反正前端只看 codeHTTP 状态码随便给不行吗行但浪费了。HTTP 4xx 和 5xx 的分工是整个互联网基础设施的共识4xx 客户端的错网关不会告警、重试也没用参数不变结果不变、前端该提示用户改输入5xx 服务器的错监控系统该告警、运维该介入、客户端重试可能恢复。参数校验失败 100% 是客户端请求的问题标 400 后APM 工具、Nginx 日志分析、前端 axios 拦截器都能自动把它归入用户输入问题处理而不是误报服务挂了。一行状态码省掉一整层沟通成本。六、异常交给全局处理业务代码零 try-catch这套机制的最终红利是Controller 和 Service 里几乎看不到 try-catch。对比一下两种写法// 示意没有全局处理器的世界每个接口都要这样包PostMapping(/api/chat)publicApiResponseChatResultchat(ValidRequestBodyChatRequestreq){try{returnApiResponse.ok(chatService.chat(...));}catch(BusinessExceptione){returnApiResponse.fail(e.getCode(),e.getMessage());}catch(Exceptione){log.error(chat error,e);returnApiResponse.fail(500,系统繁忙请稍后再试);}}19 个接口 × 每个都写一遍 维护灾难。而 AI 伙伴的实际 Controller 长这样// 项目源码ChatController节选PostMappingpublicApiResponseChatService.ChatResultchat(ValidRequestBodyChatRequestrequest){returnApiResponse.ok(chatService.chat(request.getUserId(),request.getMessage(),request.getSessionType(),Boolean.TRUE.equals(request.getNeedTts())));}干净得像伪代码。校验失败、业务错误、意外异常各走各的拦截通道横切关注点异常处理被彻底从业务代码里剥离——这就是 AOP 思想在异常处理上的落地。七、当前缺少的异常类型与补齐建议全局处理器三类拦截能兜住大局但有两类异常目前会掉进Exception 兜底体验打折IllegalArgumentExceptionService 里throw new IllegalArgumentException(openId 不能为空)这类参数问题现在会被 500 兜底返回系统繁忙——明明是客户端的错却报成服务器故障。补一个 Handler 返回 400 即可。鉴权异常项目无鉴权体系未来引入 Spring Security 或登录拦截器后AccessDeniedException/401 场景必须有专属处理否则未登录用户会看到系统繁忙而不是请先登录。补齐示例示意// 示意建议新增的两个 HandlerExceptionHandler(IllegalArgumentException.class)publicResponseEntityApiResponseVoidhandleIllegalArgument(IllegalArgumentExceptione){returnResponseEntity.badRequest().body(ApiResponse.fail(400,e.getMessage()));}八、异常 → HTTP 状态 → 业务码 → 前端处理对照表异常来源HTTP 状态业务码 codemessage前端建议处理业务成功2000success渲染 dataBusinessException(双参)400自定义如 400具体业务提示toast 展示 messageBusinessException(单参)400500具体业务提示toast 展示 messageValid 校验失败400400首个字段的校验文案高亮对应表单项未捕获异常500500“系统繁忙请稍后再试”通用错误页引导重试建议补IllegalArgumentException400400参数问题提示按输入错误处理建议补鉴权异常401/403401/403请登录/无权限跳登录页九、合规提醒异常处理是隐私泄露的常见暗门。二次开发时守住三条兜底异常的对外文案保持模糊堆栈、SQL、表结构一律不外泄log.error的日志里如果含用户对话、健康数值等敏感数据要按公司日志规范脱敏并限制留存期健康类业务错误如心率数据格式错误的 message 措辞避免下诊断结论——系统只报数据问题医疗判断永远留给专业医生。小结ApiResponse三段式 BusinessException 三类全局拦截不到一百行代码撑起了 19 个接口的统一出口。HTTP 状态码管粗分类、业务码管细分类、业务代码零 try-catch——这就是小项目也有工程尊严的样子。至此从表设计到接口出口的整条数据链路你都过了一遍接下来无论是把系统部署上线还是动手二次开发心里都有底了。

相关新闻

XXL-JOB Docker化部署全攻略:分布式任务调度平台搭建与避坑

XXL-JOB Docker化部署全攻略:分布式任务调度平台搭建与避坑

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

2026/9/24 3:55:49 阅读更多 →
太上头了,Codex已经可以识别房型图并建模了

太上头了,Codex已经可以识别房型图并建模了

AI已经能描房型图了这都行轮廓描的很到位哎路径跟随画个屋檐,做个管道虽然有点像刚毕业的助理干的活不过AI能干成这样已经很不错了,再用SKILL教一教就能做出好的建模了

2026/9/24 3:54:48 阅读更多 →
RL-赵-(六):随机逼近与随机梯度下降07:BGD、MBGD(小批量梯度下降)、SGD

RL-赵-(六):随机逼近与随机梯度下降07:BGD、MBGD(小批量梯度下降)、SGD

四、BGD, MBGD, and SGD BGD:批量梯度下降法(Batch Gradient Descent,简称BGD)是梯度下降法最原始的形式,它的具体思路是在更新每一参数时都使用所有的样本来进行更新,它的目的是得到一个全局最优解,但是每迭代一步,都要用到训练集所有的数据,如果样本数目很大,这种方…

2026/9/24 3:54:48 阅读更多 →

最新新闻

LPC2388实战指南:AMBA总线与ARM7嵌入式开发深度解析

LPC2388实战指南:AMBA总线与ARM7嵌入式开发深度解析

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

2026/9/24 4:28:13 阅读更多 →
定制柜背板 5 毫米、9 毫米、18 毫米,各用在哪

定制柜背板 5 毫米、9 毫米、18 毫米,各用在哪

背板用 5 毫米、9 毫米还是 18 毫米,先看柜子挂在哪个房间、柜深多少、跨度多长,不是越厚越合适。这是做海口全屋定制时容易被一句话带过去的构件,也容易被"加厚就是升级"的直觉带偏。欧派大家居在海口是有实体门店的连锁体系&…

2026/9/24 4:28:13 阅读更多 →
nginx-ui MCP 配置管理工具详解:让 AI Agent 安全读写 Nginx 配置文件

nginx-ui MCP 配置管理工具详解:让 AI Agent 安全读写 Nginx 配置文件

后端前端运维MCP 服务 【免费下载链接】nginx-ui Yet another WebUI for Nginx 项目地址: https://gitcode.com/gh_mirrors/ngi/nginx-ui 点击查看 免费下载 导读 本文聚焦 nginx-ui 内置的 MCP(Model Context Protocol)配置管理模块&#…

2026/9/24 4:28:13 阅读更多 →
Talos Linux ResolverConfig 配置指南:nameservers、searchDomains 与 hostDNS 全解析

Talos Linux ResolverConfig 配置指南:nameservers、searchDomains 与 hostDNS 全解析

云原生操作系统容器编排 【免费下载链接】talos Talos Linux is a modern Linux distribution built for Kubernetes. 项目地址: https://gitcode.com/gh_mirrors/ta/talos 点击查看 免费下载 本文基于 Talos Linux(v1.15 参考文档与源码)系…

2026/9/24 4:28:13 阅读更多 →
Storm 与机器学习:在线模型更新、实时预测与特征工程管道

Storm 与机器学习:在线模型更新、实时预测与特征工程管道

Storm 与机器学习:在线模型更新、实时预测与特征工程管道本文探讨了如何利用 Apache Storm 构建机器学习在线模型更新、实时预测与特征工程管道。从基础架构到具体实现,详细介绍了 Storm 与机器学习系统的集成方案,包括在线模型更新机制、实时…

2026/9/24 4:28:13 阅读更多 →
高通骁龙865救砖指南:QPST与9008模式底层刷机实战

高通骁龙865救砖指南:QPST与9008模式底层刷机实战

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

2026/9/24 4:27:12 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/9/23 9:53:41 阅读更多 →

月新闻

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

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

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

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

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

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

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

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

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

2026/9/23 9:53:40 阅读更多 →