在 Spring Boot 中构造 API 响应的最佳实践

devtools/2024/11/29 1:45:40/

在平时的开发和项目中,我们一定会涉及到接口对接的功能,由于不同开发人员的编码习惯不同,API报文在项目中通常是"百花齐放"的。

不但增加工作难度,往往也是扯皮的大头,如果能统一报文格式,不但能减少沟通成本,同时也可以减少并行开发的难度,今天我们就来介绍一种在项目中常用的API响应最佳实践,快快@你的同事,看完以后,下次对接我们直接“打默认”

01 / 为什么好的API报文结构很重要

  • 优化客户端的错误提示和处理逻辑,前端小姐姐看到了会请你吃饭
  • 减少调试时的工作量,可以更快的发现问题,避免加班引起严重脱发
  • 增加代码的可读性和维护性,避免code review的时候被老大骂

02 / 什么才是良好的API响应

结构良好的 API 响应应该是:

  • 一致:不同端点之间的格式一致。

  • 信息足够有效:包括相关数据、消息、状态代码和错误代码。

  • 简单:易于解析和理解。

    03 / 如何制定良好的结构

定义标准响应格式

首先创建所有 API 都将遵循的标准响应格式。这是一个简单而有效的格式:

图片

在此结构中, errorCode作为专门用于应用程序级错误的字段。需要注意的是,这并不意味着要取代标准 HTTP 状态代码,如200 OK 、 404 Not Found等。

相反,我们更推荐使用 HTTP 状态代码来维护标准协议级错误处理。

当应用程序需要处理更细粒度的、与业务相关的错误场景时, errorCode就会发挥作用。

例如,考虑用户的请求违反业务规则的情况 - 即使从语法或结构的角度来看该请求可能是有效的,但这应该导致400 Bad Request HTTP 状态,因为输入不符合应用程序的业务规则。

在这种情况下, errorCode将从业务逻辑的角度提供有关到底出了什么问题的更多详细信息。

例如,如果用户提交缺货产品的订单,系统将响应400 Bad Request状态并提供errorCode ,例如2000 (商品缺货)。

这使得 UI 或使用 API 的任何客户端能够通过显示适当的错误消息或提示用户采取特定操作(例如,“该产品不再可用”),以更明智和一致的方式处理情况。

这种方法确保HTTP 状态代码反映协议级别请求的总体结果,而errorCode提供用于处理应用程序内特定业务逻辑错误的附加信息。

它还简化了系统不同层的错误处理,因为errorCode可用于触发 UI 或使用 API 的任何其他服务中的特定操作。

每个字段解释:

success: 

  • 类型:boolean

  • 描述:指示 API 调用是否成功。

  • 功能:快速确定请求的结果,简化客户端逻辑。

message:

  • 类型: String

  • 描述:提供有关 API 调用结果的人类可读消息。

  • 功能:有助于向客户提供上下文反馈,对于成功和错误场景都很有用。

data: 

  • 类型: T

  • 描述:包含响应的有效负载,可以是任何数据类型。

  • 功能:提供客户请求的实际数据。

errors:

  • 类型:List<String>

  • 描述:API 调用不成功时的错误消息列表。

  • 功能:提供有关出错原因的详细信息,对于调试和用户反馈很有用。

errorCode:

  • 类型:int

  • 描述:表示错误类型的特定代码。

  • 功能:有助于以编程方式对错误进行分类并做出适当的响应。这是针对应用程序级别的错误,可能与任何业务流程错误相关,可能需要在 UI 层进行更多处理。所以这会很有帮助。但对于错误代码,我们应该始终优先考虑 HTTP 状态代码。

timestamp:

  • 类型: long

  • 描述:生成响应时的时间戳。

  • 功能:对于记录和跟踪响应时间很有用,这可以帮助调试和监控。

path:

  • 类型:String

  • 描述:被调用的 API 端点。

  • 功能:帮助识别哪个 API 端点生成了响应,对于调试和日志记录很有用。

04 / 创建实用的响应处理方法

java">
public class ResponseUtil {public static <T> ApiResponse<T> success(T data, String message, String path) {ApiResponse<T> response = new ApiResponse<>();response.setSuccess(true);response.setMessage(message);response.setData(data);response.setErrors(null);response.setErrorCode(0); // No application-level error, success scenarioresponse.setTimestamp(System.currentTimeMillis());response.setPath(path);return response;}public static <T> ApiResponse<T> error(List<String> errors, String message, int errorCode, String path) {ApiResponse<T> response = new ApiResponse<>();response.setSuccess(false);response.setMessage(message);response.setData(null);response.setErrors(errors);response.setErrorCode(errorCode);  // Application-specific error coderesponse.setTimestamp(System.currentTimeMillis());response.setPath(path);return response;}public static <T> ApiResponse<T> error(String error, String message, int errorCode, String path) {return error(Arrays.asList(error), message, errorCode, path);}
}

04 / 实现全局异常处理

全局处理异常可确保捕获任何未处理的错误并以标准响应格式返回。使用@ControllerAdvice和@ExceptionHandler注释。

java">
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public ResponseEntity<ApiResponse<Void>> handleException(HttpServletRequest request, Exception ex) {List<String> errors = Arrays.asList(ex.getMessage());ApiResponse<Void> response = ResponseUtil.error(errors, "An error occurred", 1000, return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);}@ExceptionHandler(ResourceNotFoundException.class)public ResponseEntity<ApiResponse<Void>> handleResourceNotFoundException(HttpServletRequest request, ResourceNotFoundException ex) {ApiResponse<Void> response = ResponseUtil.error(ex.getMessage(), "Resource not found", 1001, return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);}@ExceptionHandler(ValidationException.class)public ResponseEntity<ApiResponse<Void>> handleValidationException(HttpServletRequest request, ValidationException ex) {ApiResponse<Void> response = ResponseUtil.error(ex.getErrors(), "Validation failed", 1002, return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);}// Handle other specific exceptions similarly
}

05 / 在controller中使用响应格式

java">
@RestController
@RequestMapping("/api/products")
public class ProductController {@GetMapping("/{id}")public ResponseEntity<ApiResponse<Product>> getProductById(@PathVariable Long id, HttpServletRequest request) {// Fetch product by id (dummy code)Product product = productService.findById(id);if (product == null) {throw new ResourceNotFoundException("Product not found with id " + id);}ApiResponse<Product> response = ResponseUtil.success(product, "Product fetched successfully", request.getRequestURI());// Success response, no application-level errorreturn new ResponseEntity<>(response, HttpStatus.OK);}@PostMappingpublic ResponseEntity<ApiResponse<Product>> createProduct(@RequestBody Product product, HttpServletRequest request) {// Create new product (dummy code)Product createdProduct = productService.save(product);ApiResponse<Product> response = ResponseUtil.success(createdProduct, "Product created successfully", request.getRequestURI());// Success response, no application-level errorreturn new ResponseEntity<>(response, HttpStatus.CREATED);}// More endpoints...
}

06/ 定义统一的错误代码

统一的错误代码,可以避免重复的沟通,直接记录在文档中,或者保存在统一的配置中心中,上下游需要使用的时候可以直接查阅。

同时定义错误代码的过程,也是对自己逻辑重新梳理的过程,避免在编写时遗漏了边界情况Error Code Description

2000 Item out of stock2001 Payment method declined2002 Invalid coupon code2003 Order cancellation period expired2004 Account temporarily suspended2005 Multiple orders detected for the same productmark

07 / 总结

完成了这些工作,你就得到一个近乎完美的API报文结构,它干净整洁,易于维护。同时减少了巨大的沟通成本,当然好的规范是需要团队共同执行的,多和你的同事沟通,相信你们的合作会越来越顺利的


http://www.ppmy.cn/devtools/137801.html

相关文章

深入浅出剖析典型文生图产品Midjourney

2022年7月,一个小团队推出了公测的 Midjourney,打破了 AIGC 领域的大厂垄断。作为一个精调生成模型,以聊天机器人方式部署在 Discord,它创作的《太空歌剧院》作品,甚至获得了美国「数字艺术/数码摄影」竞赛单元一等奖。 这一事件展示了 AI 在绘画领域惊人的创造力,让人们…

网络安全笔记

# 网络安全概述 ### 网络安全的特征 - **机密性&#xff1a;信息不泄露给非授权的实体或对象** - **完整性&#xff1a;数据未经授权不能进行改变的特性&#xff0c; 即信息在存储或传输过程中保持不被修改&#xff0c;不被破坏的特性** - **可用性&#xff1a;可被授权实体访…

redis-cluster集群搭建

集群节点信息 192.168.222.131:46379 主要节点1 192.168.222.131:46380 从节点1 192.168.222.131:46381 从节点2192.168.222.132:46379 主要节点2 192.168.222.132:46380 从节点1 192.168.222.132:46381 从节点2192.168.222.133:46379 主要节点3 192.168.222.133:46380 从节点…

前端学习笔记

vue3的数据绑定 参考资料&#xff1a; https://blog.csdn.net/h8062651/article/details/143254339?spm1001.2014.3001.5506 Vue 3 提供了 reactive 和 ref 两个主要函数来创建响应式对象。 reactive&#xff1a; 用于创建包含多个属性的响应式对象。 当对象的属性发生变化时…

Easyui datagrid formatter 删除行失败解决

Easyui datagrid formatter 删除行失败解决 问题现象 使用formatter 为每行新增一个删除操作&#xff0c;,删除第一个行的时候没问题&#xff0c;如果存在多行的时候&#xff0c;当删除行时&#xff0c;提示错误 问题原因 因为是使用 formatter 相当于每行在渲染的时候ind…

(长期更新)《零基础入门 ArcGIS(ArcMap) 》实验二----网络分析(超超超详细!!!)

相信实验一大家已经完成了&#xff0c;对Arcgis已进一步熟悉了&#xff0c;现在开启第二个实验 ArcMap实验--网络分析 目录 ArcMap实验--网络分析 1.1 网络分析介绍 1.2 实验内容及目的 1.2.1 实验内容 1.2.2 实验目的 2.2 实验方案 2.3 实验流程 2.3.1 实验准备 2.3.2 空间校正…

python学opencv|读取图像

【1】引言 前序学习了使用matplotlib模块进行画图&#xff0c;今天开始我们逐步尝试探索使用opencv来处理图片。 【2】学习资源 官网的学习链接如下&#xff1a; OpenCV: Getting Started with Images 不过读起来是英文版&#xff0c;可能略有难度&#xff0c;所以另推荐一…

YOLOv7论文讲解和代码复现

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…