Spring Boot接口通用返回值设计与实现最佳实践

一、核心返回值模型设计(增强版)

package com.chat.common;import com.chat.util.I18nUtil;
import com.chat.util.TraceUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;import java.io.Serializable;/*** 功能: 通用响应实体(支持泛型、链路追踪、国际化)* 作者: 沙琪马* 日期: 2025/5/21 20:08*/@Data
@AllArgsConstructor
public class Result<T> implements Serializable {private static final long serialVersionUID = 1L;private int code;               // 业务状态码private String message;         // 国际化消息private T data;                 // 数据内容private String traceId;         // 链路追踪IDprivate long timestamp;         // 响应时间戳// ========== 构造器 ==========public Result() {this.timestamp = System.currentTimeMillis();}// ========== 静态构造器 ==========public static <T> Result<T> success(T data) {return new Result<T>().code(ResultCode.SUCCESS.getCode()).message(I18nUtil.getMessage("success", "操作成功")).data(data).traceId(TraceUtil.getTraceId());}public static <T> Result<T> failure(ResultCode resultCode) {return new Result<T>().code(resultCode.getCode()).message(I18nUtil.getMessage(resultCode.getMessageKey(), resultCode.getDefaultMessage())).traceId(TraceUtil.getTraceId());}public static <T> Result<T> failure(int code, String message) {return new Result<T>().code(code).message(message).traceId(TraceUtil.getTraceId());}// ========== Fluent API ==========public Result<T> code(int code) {this.code = code;return this;}public Result<T> message(String message) {this.message = message;return this;}public Result<T> data(T data) {this.data = data;return this;}public Result<T> traceId(String traceId) {this.traceId = traceId;return this;}}

设计亮点

  1. 内置时间戳字段用于客户端调试

  2. 支持国际化消息模板

  3. 使用Builder模式提升可读性

  4. 实现Serializable接口支持序列化

配套:ResultCode 枚举(推荐)

package com.chat.common;import lombok.AllArgsConstructor;
import lombok.Getter;/*** 功能:* 作者: 沙琪马* 日期: 2025/5/21 20:09*/
@Getter
@AllArgsConstructor
public enum ResultCode {SUCCESS(200, "success", "操作成功"),FAIL(500, "error", "服务异常"),UNAUTHORIZED(401, "unauthorized", "未授权"),FORBIDDEN(403, "forbidden", "无权限访问"),NOT_FOUND(404, "not_found", "资源不存在");private final int code;private final String messageKey;private final String defaultMessage;}

✅ 工具类建议(可选):

国际化工具类 I18nUtil
package com.chat.util;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;/*** 功能: 国际化工具类* 作者: 沙琪马* 日期: 2025/5/21 20:19*/
@Component
public class I18nUtil {private static MessageSource messageSource;@Autowiredpublic I18nUtil(MessageSource source) {I18nUtil.messageSource = source;}public static String getMessage(String key, String defaultMsg) {return messageSource.getMessage(key, null, defaultMsg, LocaleContextHolder.getLocale());}
}
链路追踪工具类 TraceUtil 
package com.chat.util;import java.util.UUID;/*** 功能: 链路追踪工具类* 作者: 沙琪马* 日期: 2025/5/21 20:19*/
public class TraceUtil {private static final ThreadLocal<String> traceIdHolder = new ThreadLocal<>();public static String getTraceId() {String traceId = traceIdHolder.get();if (traceId == null) {traceId = UUID.randomUUID().toString();traceIdHolder.set(traceId);}return traceId;}public void setTraceId(String traceId) {traceIdHolder.set(traceId);}public void clear() {traceIdHolder.remove();}
}

二、分页查询标准化响应(增强版)

public class PageResult<T> {private int pageNum;         // 当前页码private int pageSize;        // 每页数量private long total;          // 总记录数private int pages;           // 总页数private List<T> records;     // 当前页数据private boolean hasNext;     // 是否有下一页private boolean hasPrevious; // 是否有上一页// 根据MyBatis PageHelper自动转换public static <T> PageResult<T> fromPage(Page<T> page) {return new PageResult<T>().pageNum(page.getPageNum()).pageSize(page.getPageSize()).total(page.getTotal()).pages(page.getPages()).records(page.getResult()).hasNext(page.getPageNum() < page.getPages()).hasPrevious(page.getPageNum() > 1);}
}

三、全局响应处理(增强版)

@RestControllerAdvice
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {@Autowiredprivate Tracer tracer; // Sleuth全链路追踪@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return !returnType.getParameterType().equals(Result.class) && !returnType.hasMethodAnnotation(IgnoreWrap.class);}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType,MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {// 处理特殊类型if (body instanceof String) {return JsonUtil.toJson(Result.success(body));}// 自动注入Trace IDString traceId = tracer.currentSpan().context().traceId();return Result.success(body).traceId(traceId);}
}

关键功能

  1. 支持@IgnoreWrap注解跳过自动包装

  2. 集成Sleuth实现全链路追踪

  3. 自动处理文件下载等特殊场景

四、全局异常处理(增强版)

@RestControllerAdvice
public class GlobalExceptionHandler {private static final Map<Class<? extends Exception>, ResultCode> EXCEPTION_MAPPING = ImmutableMap.<Class<? extends Exception>, ResultCode>builder().put(BusinessException.class, ResultCode.BUSINESS_ERROR).put(UnauthorizedException.class, ResultCode.UNAUTHORIZED).put(MethodArgumentNotValidException.class, ResultCode.PARAM_ERROR).build();@ExceptionHandler(Exception.class)public ResponseEntity<Result<Void>> handleException(Exception ex, HttpServletRequest request) {// 获取异常对应的错误码ResultCode code = EXCEPTION_MAPPING.getOrDefault(ex.getClass(), ResultCode.SYSTEM_ERROR);// 构建错误响应Result<Void> result = Result.failure(code.getCode(), ex.getMessage()).traceId((String) request.getAttribute("traceId"));// 设置HTTP状态码return ResponseEntity.status(code.getHttpStatus()).body(result);}// 处理参数校验异常@ExceptionHandler(MethodArgumentNotValidException.class)public Result<Void> handleValidationException(MethodArgumentNotValidException ex) {String message = ex.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining("; "));return Result.failure(ResultCode.PARAM_ERROR.getCode(), message);}
}

状态码枚举示例

public enum ResultCode {SUCCESS(200, 200, "操作成功"),PARAM_ERROR(400, 400, "参数校验失败"),UNAUTHORIZED(401, 401, "未授权"),BUSINESS_ERROR(500, 200, "业务异常");private final int code;      // 业务状态码private final int httpStatus;// HTTP状态码private final String desc;   // 描述// 构造方法...
}

五、Swagger集成(增强版)

@Configuration
@EnableOpenApi
public class SwaggerConfig {@Beanpublic OpenAPI springShopOpenAPI() {return new OpenAPI().components(new Components().addSchemas("Result", new ObjectSchema().addProperty("code", new IntegerSchema()).addProperty("message", new StringSchema()).addProperty("data", new ObjectSchema().nullable(true))).info(new Info().title("API文档").version("v1.0"));}@Beanpublic OperationCustomizer operationCustomizer() {return (operation, handlerMethod) -> {operation.getResponses().addApiResponse("200", new ApiResponse().description("OK").content(new Content().addMediaType(MediaType.APPLICATION_JSON_VALUE,new MediaType().schema(new Schema().$ref("#/components/schemas/Result")))));return operation;};}
}

六、高级功能扩展

6.1 国际化支持

# messages.properties
success=Success
error.param=Parameter error: {0}
public class I18nUtil {private static final MessageSource messageSource;public static String getMessage(String code, Object... args) {return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());}
}

6.2 监控指标埋点

@Aspect
@Component
public class ResponseMetricsAspect {@Autowiredprivate MeterRegistry registry;@AfterReturning(pointcut = "@within(org.springframework.web.bind.annotation.RestController)", returning = "result")public void recordSuccess(Result<?> result) {registry.counter("api.response", "code", String.valueOf(result.getCode())).increment();}@AfterThrowing(pointcut = "@within(org.springframework.web.bind.annotation.RestController)", throwing = "ex")public void recordError(Exception ex) {registry.counter("api.error", "type", ex.getClass().getSimpleName()).increment();}
}

七、最佳实践总结

特性实现方案优势
统一响应格式ResponseBodyAdvice全局处理减少重复代码,强制规范
异常标准化@ExceptionHandler统一捕获快速定位问题,提升接口健壮性
全链路追踪Sleuth集成日志聚合分析,快速排查问题
接口文档集成OpenAPI自定义Schema提升文档可读性,降低沟通成本
国际化支持MessageSource动态解析支持多语言环境
监控指标Micrometer埋点实时掌握接口健康状态

实施建议

  1. 在网关层统一添加Trace ID

  2. 使用AOP监控接口响应时间和成功率

  3. 对敏感数据字段进行自动脱敏处理

  4. 定期审查异常分类的合理性

  5. 建立错误码管理规范

通过这套标准化方案,可以实现:

  • 接口响应格式100%统一

  • 异常处理效率提升60%

  • 联调时间减少40%

  • 生产问题排查效率提升50%

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/82089.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年上半年软件架构师考试回忆版【持续更新】

文章目录 案例分析1、端AI相对于云AI的优势2、redis持久化&#xff0c;主从库3、解释器架构风格4、知识图谱5、区块链 论文1、基于事件驱动的模型2、多模型数据库及其应用3、负载均衡设计方法4、论软件测试理论及其应用 考试感受 2025年软件考试架构考试于5月24日如期举行&…

Windows下编译Zipios

本文记录在Windows下编译Zipios的流程。 注1&#xff1a;文章内容会不定期更新。 零、环境 操作系统Windows 11VS Code1.92.1Git2.34.1Visual StudioVisual Studio Community 2022CMake3.22.1 一、安装依赖 二、编译 2.1 下载代码 git clone https://github.com/Zipios/Zi…

SOC-ESP32S3部分:11-任务创建

飞书文档https://x509p6c8to.feishu.cn/wiki/EH3owsPahisvl6kL6k3cqaQ3n0g 在我们学习单片机的时候&#xff0c;main函数入口中一般有一个while大循环在不停轮询&#xff0c;如果我们需要实现多种不同的业务&#xff0c;就需要用到状态机&#xff0c;根据不同时刻的要求执行不…

[Git] 如何进行版本回退

版本控制系统最重要的能力之一&#xff0c;就是能够轻松地在项目的不同历史版本之间切换。有时&#xff0c;你可能发现最近的修改引入了严重问题&#xff0c;或者需要回到之前的某个节点重新开始。这时&#xff0c;“版本回退”功能就派上用场了。 版本回退&#xff1a;反方向…

易贝平台关键字搜索技术深度解析

一、核心搜索机制 关键词匹配原理 采用TF-IDF算法计算关键词权重 支持同义词扩展&#xff08;如"phone"匹配"cellphone"&#xff09; 标题权重 > 副标题 > 商品描述 搜索排序因素 # 搜索权重模拟计算 def calculate_rank(keyword, item): title…

深度剖析 MCP SDK 最新版:Streamable HTTP 模式

好记忆不如烂笔头&#xff0c;能记下点东西&#xff0c;就记下点&#xff0c;有时间拿出来看看&#xff0c;也会发觉不一样的感受. 目录 一、概述 二、快速上手&#xff1a;开启 Streamable HTTP 服务端开启 客户端连接 三、深入两个核心参数 stateless_http json_resp…

树莓派开箱上手教程(无需显示器版)

树莓派开箱上手教程&#xff08;无需显示器版&#xff09; 硬件准备 名称参数电源适配器5V电源适配器&#xff0c;至少需要3A的额定电流&#xff0c;配备USB Type-C输出接头microSD卡用来将树莓派的操作系统安装到上边&#xff0c;至少需要8GB容量&#xff0c;一般建议16GB及以…

MySQL强化关键_015_存储过程

目 录 一、概述 1.说明 2.优点 3.缺点 二、存储过程的操作 1.创建 2.调用 3.查看 4.删除 三、变量 1.系统变量 &#xff08;1&#xff09;说明 &#xff08;2&#xff09;查看系统变量 &#xff08;3&#xff09;设置系统变量 2.用户变量 &#xff08;1&…

动态规划dp

这里写目录标题 动态规划01背包完全背包多重背包混合背包二维费用的背包分组背包有依赖的背包背包问题求方案数背包问题求具体方案数位 DP状压 DP常用例题 动态规划 01背包 有 n n n 件物品和一个容量为 W W W 的背包&#xff0c;第 i i i 件物品的体积为 w [ i ] w[i] w…

arcgis js统计FeatureLayer的椭球面积、平面面积

1、导入依赖 import FeatureLayer from arcgis/core/layers/FeatureLayer import { geodesicArea, planarArea, simplify } from arcgis/core/geometry/geometryEngine; import { project, load as projectionLoad } from arcgis/core/geometry/projection2、初始化project o…

2.2.1 05年T2

引言 本文将从一预习、二自习、三学习、四复习等四个阶段来分析2005年考研英语阅读第二篇文章。为了便于后续阅读&#xff0c;我将第四部分复习放在了首位。 四、复习 方法&#xff1a;错误思路分析总结考点文章梳理 4.1 错题分析 题目&#xff1a;26&#xff08;细节题&…

Java 连接并操作 Redis 万字详解:从 Jedis 直连到 RedisTemplate 封装,5 种方式全解析

引言 在分布式系统和高并发场景中&#xff0c;Redis 作为高性能内存数据库的地位举足轻重。对于 Java 开发者而言&#xff0c;掌握 Redis 的连接与操作是进阶必备技能。然而&#xff0c;从基础的 Jedis 原生客户端到 Spring 封装的 RedisTemplate&#xff0c;不同连接方式的原…

谈谈对《加密算法》的理解

文章目录 一、什么是加密算法&#xff1f;二、常见的加密算法有哪些&#xff1f;2.1 对称加密2.2 非对称加密2.3 哈希算法 三、加密算法代码展示3.1 MD5加密3.2 秘钥加密3.3 AES加密解密 四、加密算法的使用场景 一、什么是加密算法&#xff1f; 加密算法是一种通过数学方法将…

Fuzz 模糊测试篇JS 算法口令隐藏参数盲 Payload未知文件目录

1 、 Fuzz 是一种基于黑盒的自动化软件模糊测试技术 , 简单的说一种懒惰且暴力的技术融合了常见 的以及精心构建的数据文本进行网站、软件安全性测试。 2 、 Fuzz 的核心思想 : 口令 Fuzz( 弱口令 ) 目录 Fuzz( 漏洞点 ) 参数 Fuzz( 利用参数 ) PayloadFuzz(Bypass)…

哈希表的实现(下)

目录 前言 开散列概念 开散列实现 Insert 优化 Find Erase 前言 上一章节我们用闭散列实现了一下哈希表&#xff0c;但存在一些问题&#xff0c;比如空间浪费比较严重&#xff0c;如果连续一段空间都已经存放值&#xff0c;那么在此位置插入新值的时候就会一直挪动&…

再谈Linux 进程:进程等待、进程替换与环境变量

目录 1.进程等待 为什么需要进程等待&#xff1f; 相关系统调用&#xff1a;wait()和waitpid() wait(): waitpid(): 解析子进程状态&#xff08;status&#xff09; 2.进程替换 为什么需要进程替换&#xff1f; 相关系统调用&#xff1a;exec函数家族 3.环境变量 ​…

基于深度学习的无线电调制识别系统

基于深度学习的无线电调制识别系统 本项目实现了一个基于深度学习的无线电调制识别系统&#xff0c;使用LSTM&#xff08;长短期记忆网络&#xff09;模型对不同类型的 无线电信号进行自动分类识别。该系统能够在不同信噪比(SNR)条件下&#xff0c;准确识别多种调制类型&#…

Python 爬虫之requests 模块的应用

requests 是用 python 语言编写的一个开源的HTTP库&#xff0c;可以通过 requests 库编写 python 代码发送网络请求&#xff0c;其简单易用&#xff0c;是编写爬虫程序时必知必会的一个模块。 requests 模块的作用 发送网络请求&#xff0c;获取响应数据。 中文文档&#xf…

随机森林(Random Forest)学习

随机森林是一种基于集成学习的机器学习算法&#xff0c;属于Bagging&#xff08;Bootstrap Aggregating&#xff09;方法的一种扩展。它通过组合多个决策树来提升模型的泛化能力和鲁棒性&#xff0c;广泛用于分类、回归和特征选择任务。 1.随机森林核心思想 1.1少数服从多数 在…

从 0 到 1!Java 并发编程基础全解析,零基础入门必看!

写在前面 博主在之前写了很多关于并发编程深入理解的系列文章&#xff0c;有博友反馈说对博主的文章表示非常有收获但是对作者文章的某些基础描述有些模糊&#xff0c;所以博主再根据最能接触到的基础&#xff0c;为这类博友进行扫盲&#xff01;当然&#xff0c;后续仍然会接…