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

embedded/2024/11/28 21:45:25/

在平时的开发和项目中,我们一定会涉及到接口对接的功能,由于不同开发人员的编码习惯不同,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/embedded/141269.html

相关文章

【设计模式】【行为型模式(Behavioral Patterns)】之责任链模式(Chain of Responsibility Pattern)

1. 设计模式原理说明 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09; 是一种行为设计模式&#xff0c;它允许你将请求沿着处理者链进行发送。每个处理者都可以处理请求&#xff0c;或者将其传递给链上的下一个处理者。这种模式使得多个对象都有机会处理请…

67 mysql 的 间隙锁

前言 我们这里主要是 来看一下 mysql 中的 间隙锁 间隙锁 主要存在的地方一般就是在 查询主键查询不到, 索引查询查询不到 的场景 然后 我们这里来调试一下 这里的整个流程, 间隙锁的加锁 以及 间隙锁的使用, 以及 间隙锁的释放 从逻辑上来说 间隙锁 锁定的是一个区间, 按照…

mysql-分析并解决mvcc更新丢失问题

多版本并发控制&#xff08;Multi-Version Concurrency Control, MVCC&#xff09;是现代数据库系统中常用的一种并发控制机制&#xff0c;用于提高并发性能和数据一致性。然而&#xff0c;MVCC 本身并不能完全解决更新丢失问题。让我们详细探讨一下这个问题的原因和背景。 更…

大语言模型(LLM)的训练微调 Fine Tuning -- part3 本地调用

以下代码示范如何调用已经微调后的大语言模型&#xff0c;调用本地模型 先决条件 已经有了本地训练好的大语言模型&#xff0c;如何训练可以参考我的博文 《生成式 AI》课程 作业6 大语言模型&#xff08;LLM&#xff09;的训练微调 Fine Tuning -- part2-CSDN博客文章浏览阅…

IDEA全局设置-解决maven加载过慢的问题

一、IDEA全局设置 注意&#xff1a;如果不是全局设置&#xff0c;仅仅针对某个项目有效&#xff1b;例在利用网上教程解决maven加载过慢的问题时&#xff0c;按步骤设置却得不到解决&#xff0c;原因就是没有在全局设置。 1.如何进行全局设置 a.在项目页面&#xff0c;点击f…

计算机网络 第4章 网络层

计算机网络 &#xff08;第八版&#xff09;谢希仁 第 4 章 网络层4.2.2 IP地址**无分类编址CIDR**IP地址的特点 4.2.3 IP地址与MAC地址4.2.4 ARP 地址解析协议4.2.5 IP数据报的格式题目2&#xff1a;IP数据报分片与重组题目&#xff1a;计算IP数据报的首部校验和(不正确未改) …

【前端学习笔记】Web API——BOM与DOM

前置知识 JavaScript ECMAscript BOM DOM ECMAscript&#xff1a;相当于JS的执行标准。 BOM&#xff1a;浏览器对象模型。BOM 提供了与浏览器窗口交互的接口&#xff0c;这些接口独立于网页内容。BOM的对象允许JavaScript访问和操作浏览器窗口&#xff0c;是所有JavaScri…

上海迪士尼奇幻冬日巡游:IP营销如何出圈?

迪士尼&#xff0c;作为全球娱乐产业的巨头&#xff0c;一直以其卓越的IP营销能力闻名于世。而其推出的“迪士尼奇幻冬日巡游”&#xff0c;无疑再次展示了其深厚的营销功底和独特的IP魅力。上海迪士尼在品牌营销上如何出圈&#xff0c;吸引忠实消费者&#xff0c;成为了一个值…