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

在平时的开发和项目中,我们一定会涉及到接口对接的功能,由于不同开发人员的编码习惯不同,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 / 创建实用的响应处理方法


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注释。


@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中使用响应格式


@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.mzph.cn/web/61055.shtml

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

相关文章

多目标优化算法——多目标粒子群优化算法(MOPSO)

Handling Multiple Objectives With Particle Swarm Optimization&#xff08;多目标粒子群优化算法&#xff09; 一、摘要&#xff1a; 本文提出了一种将帕累托优势引入粒子群优化算法的方法&#xff0c;使该算法能够处理具有多个目标函数的问题。与目前其他将粒子群算法扩展…

OpenCV与AI深度学习|16个含源码和数据集的计算机视觉实战项目(建议收藏!)

本文来源公众号“OpenCV与AI深度学习”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;分享&#xff5c;16个含源码和数据集的计算机视觉实战项目 本文将分享16个含源码和数据集的计算机视觉实战项目。具体包括&#xff1a; 1. 人…

Android导出Excel

poi org.apache.poi:poi-ooxml:4.x&#xff1a; 不支持Android使用&#xff0c; 不支持原因&#xff1a;Android底层库不支持xml所需的bean类&#xff0c;使用即报错only supported starting with Android O (–min-api 26) org.apache.poi:poi-ooxml:5.2.0&#xff1a; 支持A…

简历面-面经03

面经04 项目细则 项目名称&#xff1a;基于鸿蒙的爱心云捐赠系统 鸿蒙开发 ArkTS语言 ArkTS在继承TypeScript语法的基础上进行了优化&#xff0c;以提供更高的性能和开发效率。 目前流行的编程语言TypeScript是在JavaScript基础上通过添加类型定义扩展而来的&#xff0c;而A…

Jenkins升级到最新版本后无法启动

1. 场景还原 最近在web界面将jenkins升级到最新版本后&#xff0c;后台无法启动jenkins服务&#xff0c;服务状态如下&#xff1a; 运行jenkins命令提示invalid Java version jenkins --version jenkins: invalid Java version: java version "1.8.0_202" Java(TM)…

【计算机视觉+MATLAB】自动检测并可视化圆形目标:通过 imfindcircles 和 viscircles 函数

引言 自动检测图像中的圆形或圆形对象&#xff0c;并可视化检测到的圆形。 函数详解 imfindcircles imfindcircles是MATLAB中的一个函数&#xff0c;用于在图像中检测并找出圆形区域。 基本语法&#xff1a; [centers, radii] imfindcircles(A, radiusRange) [centers, r…

鸿蒙NEXT元服务:利用App Linking实现无缝跳转与二维码拉起

【效果】 元服务链接格式&#xff08;API>12适用&#xff09;&#xff1a;https://hoas.drcn.agconnect.link/ggMRM 生成二维码后效果&#xff1a; 【参考网址】 使用App Linking实现元服务跳转&#xff1a;文档中心 草料二维码&#xff1a;草料二维码生成器 【引言】 …

Matlab读取4-D uint8变量需要转变成double型变量

Matlab读取4-D uint8变量需要转变成double型变量 如rgbData是一个4-D uint8变量&#xff0c;里面是像素数据&#xff0c;读取的代码如下&#xff1a; R double(rgbData(z, x, y, 1)); G double(rgbData(z, x, y, 2)); B double(rgbData(z, x, y, 3)); inscribedSpherePixe…

下载安装Android Studio

&#xff08;一&#xff09;Android Studio下载地址 https://developer.android.google.cn/studio 滑动到 点击下载文档 打开新网页 切换到english ![](https://i-blog.csdnimg.cn/direct/b7052b434f9d4418b9d56c66cdd59fae.png 等待一会&#xff0c;出现 点同意后&#xff0…

【C/C++】深入解析 Stack 与 Queue 数据结构(详解):实现原理、应用场景与性能优化

文章目录 引言栈&#xff08;Stack&#xff09;数据结构详解1. 栈的基本概念2. 栈的实现原理3. C中的栈实现4. 栈的应用场景5. 栈的性能分析6. 实战示例&#xff1a;括号匹配 队列&#xff08;Queue&#xff09;数据结构详解1. 队列的基本概念2. 队列的实现原理3. C中的队列实现…

Flutter:encrypt插件 AES加密处理

1、pubspec.yaml导入插件 cupertino_icons: ^1.0.8 # 密码加密 encrypt: 5.0.3encrypt封装 import package:encrypt/encrypt.dart; /// 加密类 class EncryptUtil {static final EncryptUtil _instance EncryptUtil._internal();factory EncryptUtil() > _instance;Encrypt…

深入理解HTML基本结构:构建现代网页的基石

深入理解HTML基本结构&#xff1a;构建现代网页的基石 在数字时代&#xff0c;HTML&#xff08;超文本标记语言&#xff09;是构建和设计网页的基础。了解HTML的基本结构对于任何希望掌握网页开发的人来说至关重要。本文将详细介绍HTML文件的基本骨架&#xff0c;包括其核心标…

【css实现收货地址下边的平行四边形彩色线条】

废话不多说&#xff0c;直接上代码&#xff1a; <div class"address-block" ><!-- 其他内容... --><div class"checked-ar"></div> </div> .address-block{height:120px;position: relative;overflow: hidden;width: 500p…

从零开始配置Qt+VsCode环境

从零开始配置QtVsCode环境 文章目录 从零开始配置QtVsCode环境写在前面扩展安装及配置Qt Configure配置 VsCode创建Qt工程VsCodeQMakeMinGwVsCodeQMakeMsvcVsCodeCMakeMinGwVsCodeCMakeMsvcQtCreatorQMakeMinGw->VsCodeQtCreatorQMakeMsvc->VsCodeQtCreatorCMakeMinGw-&g…

【前端】JavaScript中的字面量概念与应用详解

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 &#x1f4af;前言&#x1f4af;字面量1. 数字字面量2. 字符串字面量3. 布尔字面量4. 空值字面量&#xff08;null&#xff09;5. 对象字面量6. 数组字面量7. 正则表达式字面量8. 特殊值字面量9. 函数字…

使用Spring Data MongoDB中的MongoTemplate实现分组查询最新的数据

假设我们的mongoDB数据库中有一个集合名为stents的集合&#xff0c;我们想查询出每个支架号对应的最新的一条数据&#xff0c;即有多少个支架就会有对应多少条最新的支架数据。 我们使用Spring Data MongoDB中的MongoTemplate来查询所有支架的最新数据&#xff0c;可以通过构建…

nodejs import 导入module.exports = xxx的模块, 在ES6项目中导入commonJs规范的require模块

在 Node.js 模块化开发中&#xff0c;import遵循的是ES6规范&#xff0c;require遵循的是CommonJS规范&#xff0c;他们都用于加载模块&#xff0c;当我们的项目是使用es6规范时&#xff0c; 需要使用一个地方的commonJs规范的模块&#xff0c; 我们就可以使用 import xxx …

STM32 外设简介

STM32 外设简介 STM32 是由意法半导体 (STMicroelectronics) 开发的一系列基于 ARM Cortex 内核的微控制器&#xff0c;广泛应用于嵌入式系统中。STM32 系列的一个重要特点是其丰富而强大的外设模块&#xff0c;支持多种接口和功能&#xff0c;能满足工业控制、物联网、消费电…

Python使用ffmpeg进行本地视频拉流,并使用训练模型识别人脸,并将识别后的模型推流源码

前言&#xff1a; Windows上搭建nginx-rtsp流媒体服务器&#xff0c;实现FFmpeg推流、录像转rtsp推流 - WayWayWayne - 博客园参考上述文章和一些webRTC前端拉流文章 主要是缕一缕思路和每个部分的代码功能&#xff0c;文件命名高度相似导致。 效果&#xff1a; 代码&#x…

Java面经之JVM

[1]. 说说双亲委派机制&#xff1f; 类的加载器主要有启动类加载器、拓展类加载器、应用程序类加载器、用户自定义类加载器。 当Java程序需要加载一个类时&#xff0c;类加载器首先会检查是否已经加载过该类&#xff0c;如果已经加载过&#xff0c;则直接返回该类&#xff1b…