【业务功能篇48】后端接口开发的统一规范

业务背景:日常工作中,我们开发接口时,一般都会涉及到参数校验、异常处理、封装结果返回等处理。而我们项目有时为了快速迭代,在这方面上有所疏忽,后续导致代码维护比较难,不同的开发人员的不同习惯,各式各样的接口返回,所以专门进行了一次业务整改,统一规范专项

如果每个后端开发在参数校验、异常处理等都是各写各的,没有统一处理的话,代码就不优雅,也不容易维护。所以,作为一名合格的后端开发工程师,我们需要统一校验参数,统一异常处理、统一结果返回,让代码更加规范、可读性更强、更容易维护。

接口统一响应对象返回

作为后端开发,我们项目的响应结果,需要统一标准的返回格式。一般一个标准的响应报文对象,有一下几个属性

  • code :响应状态码
  • message :响应结果描述
  • data:返回的数据

1)响应状态码一般用枚举表示:

包结构: com.xxx.common.core.domain.ResultCode

/*** 响应状态码**/
public enum ResultCode {/**操作成功**/SUCCESS("200","操作成功"),/**操作失败**/ERROR("500","操作失败"),;/*** 自定义状态码**/private String code;/**自定义描述**/private String message;ResultCode(String code, String message) {this.code = code;this.message = message;}public String getCode() {return code;}public String getMessage() {return message;}
}

2)因为返回的数据类型不是确定的,我们可以使用泛型,如下:

包结构:com.xxx.common.core.domain.BaseResponse

/*** 响应结果封装对象**/
public class BaseResponse<T> implements Serializable {private static final long serialVersionUID = 1901152752394073986L;/*** 响应状态码*/private String code;/*** 响应结果描述*/private String message;/*** 返回的数据*/private T data;/*** 成功返回* @param data* @return: com.msb.hjycommunity.common.core.domain.BaseResponse<T>*/public static <T> BaseResponse<T> success(T data){BaseResponse<T> response = new BaseResponse<>();response.setCode(ResultCode.SUCCESS.getCode());response.setMessage(ResultCode.SUCCESS.getMessage());response.setData(data);return response;}/*** 失败返回* @param message* @return: com.msb.hjycommunity.common.core.domain.BaseResponse<T>*/public static <T> BaseResponse<T> fail(String message){BaseResponse<T> response = new BaseResponse<>();response.setCode(ResultCode.ERROR.getCode());response.setMessage(message);return response;}/*** 失败返回* @param message* @return: com.msb.hjycommunity.common.core.domain.BaseResponse<T>*/public static <T> BaseResponse<T> fail(String code, String message){BaseResponse<T> response = new BaseResponse<>();response.setCode(code);response.setMessage(message);return response;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public T getData() {return data;}public void setData(T data) {this.data = data;}
}

3)测试

  • 创建user实体类
public class User {private String userId;private String username;public User() {}public User(String userId, String username) {this.userId = userId;this.username = username;}public String getUserId() {return userId;}public void setUserId(String userId) {this.userId = userId;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}
}

 

  • 创建UserController
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/queryUserById")public BaseResponse<User> queryUserById(String userId){if(userId != null){return BaseResponse.success(new User(userId,"spike"));}else{return BaseResponse.fail("查询用户信息失败!");}}
}

 

  • 测试
http://localhost:8888/user/queryUserById?userId=1//output
{"code": "200","message": "操作成功","data": {"userId": "1","username": "tom"}
}http://localhost:8888/user/queryUserById//output
{"code": "500","message": "查询用户信息失败!","data": null
}

使用注解,统一参数校验

我们在实际的开发过程中经常会遇到需要对参数进行校验的情况,比如在需要用户输入手机号的时候他是不是真的输入了一个合法的手机号,在需要用户输入一个邮箱的时候他是不是真的输入了一个合法的邮箱,用户输入的内容是不是超出了长度限制等等。

当一个表单的数据较多的时候,单纯的数据校验代码就会占到很大的幅度,所以简化基础数据的校验可以省去我们很多的工作,让我们能更专注于功能的实现。

接下来我们一起看一下 springboot中参数校验(validation)的使用,首先导入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Validator可以非常方便的制定校验规则,并自动帮你完成校验。首先在入参里需要校验的字段加上注解,每个注解对应不同的校验规则,并可制定校验失败后的信息:


public class User {private String userId;@NotNull(message = "username 不能为空")private String username;
}

 

校验规则和错误提示信息配置完毕后,接下来只需要在接口需要校验的参数上加上@Validated注解,并添加BindResult参数即可方便完成验证:


@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/addUser")public BaseResponse addUser(@Validated User user, BindingResult bindingResult){List<FieldError> fieldErrors = bindingResult.getFieldErrors();//如果参数校验失败,会将错误信息封装成对象组装在 BindingResultif(!fieldErrors.isEmpty()){return BaseResponse.fail(fieldErrors.get(0).getDefaultMessage());}return BaseResponse.success("OK");}
}

 

测试

http://localhost:8888/user/addUser?username="tom"{"code": "500","message": "userId 不能为空","data": null
}http://localhost:8888/user/addUser?username="tom"&userId=1{"code": "200","message": "操作成功","data": "OK"
}

内置的校验注解有很多,罗列如下:

统一异常处理

日常开发中,我们一般都是自定义统一的异常类

  • 自定义异常可以携带更多的信息。
  • 项目开发中经常是很多人负责不同的模块,使用自定义异常可以统一了对外异常展示的方式。
  • 自定义异常语义更加清晰明了,一看就知道是项目中手动抛出的异常。

自定义异常类, 所在包结构: com.xxx.common.core.exception.BaseException


/*** 基础异常**/
public class BaseException extends RuntimeException{/*** 错误码*/private String code;/*** 错误消息*/private String defaultMessage;public BaseException() {}public BaseException(String code, String defaultMessage) {this.code = code;this.defaultMessage = defaultMessage;}public String getCode() {return code;}public String getDefaultMessage() {return defaultMessage;}
}

 

在controller 层,我们来使用这个自定义异常:


@RequestMapping("/queryUser")
public BaseResponse queryUser(User user){//模拟查询失败抛出异常throw new BaseException("500","测试异常类!");
}

 

这块代码,没什么问题,但是如果在很多地方都抛出这个异常,我们处理起来就会比较麻烦。

这时我们可以借助注解@RestControllerAdvice,让代码更优雅。@RestControllerAdvice是一个应用于Controller层的切面注解,它一般配合@ExceptionHandler注解一起使用,作为项目的全局异常处理。示例代码如下:


/*** 全局异常处理器**/
@RestControllerAdvice
public class GlobalExceptionHandler {}

我们有想要拦截的异常类型,比如想拦截BaseException类型,就新增一个方法,使用@ExceptionHandler注解修饰,并指定你想处理的异常类型,接着在方法内编写对该异常的操作逻辑,就完成了对该异常的全局处理! 


@RestControllerAdvice
public class GlobalExceptionHandler {/*** 基础异常* @param e* @return: com.xxx.common.core.domain.BaseResponse*/@ExceptionHandler(BaseException.class)@ResponseBodypublic BaseResponse baseExceptionHandler(BaseException e){return BaseResponse.fail(e.getDefaultMessage());}}

 

测试

http://localhost:8888/hejiayun/user/queryUser{"code": "500","message": "测试异常类!","data": null
}

总结:整改的几个维度,但这里需要注意的是,全局异常设计,在个人看来并不一定是有效的,有时会封装过简单,还不易于排查代码问题。

  • 通过Validation 完成了方便的参数校验
  • 通过全局异常处理 + 自定义异常完成了异常操作的规范
  • 通过数据统一响应完成了响应数据的规范
  • 多个方面组装非常优雅的完成了后端接口的协调,让开发人员有更多的经历注重业务逻辑代码,轻松构建后端接口。

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

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

相关文章

2.3 QLayout一探究竟(上)

2.布局 2.1 布局简介 2.2 Qt四大布局介绍及使用方法 2.2.1 QBoxLayout讲解及示例 2.2.1.1 QHBoxLayout介绍 2.2.1.1 QVBoxLayout介绍 2.2.2 QFormLayout讲解及示例 2.2.3 QGridLayout讲解及示例 2.2.4 QStackedLayout讲解及示例 2.3 QLayout一探究竟 2.3.1 布局的基础单元-QL…

HTTP进化史:从HTTP1的简单到HTTP3的强大

文章目录 &#x1f4c8;I. HTTP1⚡A. 基本特点⚡B. 特点⚡C. 优缺点 &#x1f4c8;II. HTTP2⚡A. 基本特点⚡B. 特点⚡C. 优缺点 &#x1f4c8;III. HTTP3⚡A. 基本特点⚡B. 特点⚡C. 优缺点 &#x1f4c8;IV. 总结&#x1f4c8;附录&#xff1a;「简历必备」前后端实战项目&am…

[RK3568] AMP架构

Rockchip 平台异构多系统 AMP&#xff08;非对称多核架构&#xff09;的开发软件包&#xff0c;支持 Linux(Kernel-4.19)、 Baremetal(HAL)、RTOS(RT-Thread) 组合AMP构建形式。 Baremetal(HAL) Baremetal表示裸机操作系统&#xff0c;HAL是裸机操作系统的一种。 裸机嵌入式系…

MySQL不适合创建索引的7种情况

1. 在where中使用不到的字段&#xff0c;不要设置索引 WHERE条件&#xff08;包括order by &#xff0c;group by&#xff09; 2. 数据量小的表最好不要使用索引 表记录太少&#xff0c;比如少于1000个&#xff0c;创建索引会先查索引&#xff0c;再回表&#xff0c;查询花费…

01 QT命名规范,快捷键

命名规范&#xff1a; 类名首字母大写&#xff0c;单词和单词之间首字母大写函数名、变量名 首字母小写&#xff0c;单词和单词之间首字母大写 快捷键&#xff1a; 运行&#xff1a;ctrl r 编译&#xff1a;ctrl b 注释&#xff1a;ctrl / 字体缩放&#xff1a;ctrl 鼠…

设计模式之工厂模式-简单工厂、工厂方法、抽象工厂模式

工厂模式是一种常见的创建型设计模式&#xff0c;它提供了一种创建对象的方法&#xff0c;将对象的创建与使用分离开来&#xff0c;可以减少代码的耦合度。在工厂模式中&#xff0c;我们通过一个工厂类来创建对象&#xff0c;而不是直接在代码中实例化对象。 工厂模式有三种常…

C++并发编程之共享数据(二)

3.1 条件竞争 恶性条件竞争通常发生于完成对多于一个的数据块的修改。例如对一个双向链表的结点的修改。该节点有两个指针。 避免条件竞争的两种方式&#xff1a; 方式一&#xff1a;确保只有进行修改的线程才能看到不变量被破坏时的中间状态。从其他访问线程的角度来 看&…

Git 常用命令

Git操作 克隆远程仓库到本地 # Git初始化&#xff08;仅一次使用的适合需要设置&#xff09; git config --global user.name "用户名" git config --global user.email "邮箱账号"# 朴实无华的拉取 git clone <url>分支 # 查看分支 git branch # …

Spring Boot 配置属性设置优先级

文章首发地址 Spring Boot设计了非常特殊的加载指定属性文件&#xff08;PropertySource&#xff09;的顺序&#xff0c;以允许对属性值进行合理的覆盖。属性值会以下面的优先级进行设置。 home目录下的Devtools全局设置属性&#xff08;~/.spring-boot-devtools.properties&a…

【每日随笔】摩托车安全驾驶 ② ( 头盔选择 | 新手上路技巧 | 摩托车驾驶速度 | 保命法则 | 骑车心理态度 )

文章目录 一、摩托车安全驾驶0、头盔选择1、新手上路技巧2、摩托车驾驶速度3、保命法则4、骑车心理态度 重要的保命法则 : ① 不变道 : 前方 有车 , 就停车 , 等他走你再走 , 千万不要随意变道 , 摩托车变道很危险 , 一个不好就进骨科 , 而且还是你自己全责 ; 只有在 左转 ( …

Puppeteer 使用教程-实战篇(爬取图片、视频、音频,页面数据)

目录 前言 一、 获取实体店铺信息 二、 获取全国各省市县地图json数据 三、 cookies 四、 获取网络图片、视频资源 五、 自动化测试 总结 前言 续上篇&#xff0c;我们简单讲述一下puppeteer常见的应用场景&#xff0c;包括静态页面数据获取&#xff0c;网络请求获取截取…

第 5 章 Spark Shuffle 解析

第 5 章 Spark Shuffle 解析 5.1 Shuffle 的核心要点1. 数据分区&#xff1a;2.数据传输&#xff1a;3. 数据排序&#xff1a;4.数据聚合&#xff1a;5. 数据重分发&#xff1a;6.数据持久化&#xff1a;5.1.1 ShuffleMapStage 与 ResultStage 5.2 HashShuffle 解析5.2.1 未优化…

day7-三数之和

三数之和 力扣题目链接(opens new window) 给你一个包含 n 个整数的数组 nums&#xff0c;判断 nums 中是否存在三个元素 a&#xff0c;b&#xff0c;c &#xff0c;使得 a b c 0 &#xff1f;请你找出所有满足条件且不重复的三元组。 注意&#xff1a; 答案中不可以包含…

Appium python 框架

目录 前言 流程 结构 具体说说 run.py 思路 其他模块 前言 Appium是一个开源的移动应用自动化测试框架&#xff0c;它允许开发人员使用多种编程语言&#xff08;包括Python&#xff09;来编写自动化测试脚本。Appium框架提供了一套API和工具&#xff0c;可以与移动设备进…

基于单片机语音识别智能家居系统的设计与实现

功能介绍 以STM32单片机作为主控系统&#xff1b;液晶显示当前环境温湿度&#xff0c;用电器开关状态通过语音模块识别设定的语音&#xff1b;DHT11进行环境温湿度采集&#xff1b;通过语音播报模块报当前温湿度&#xff0c;智能回复通过语音识别可以打开灯&#xff0c;窗帘&am…

C语言-排序,初识指针

目录 【1】冒泡排序&#xff08;从小到大&#xff09; 【2】选择排序 【3】二维数组 【4】指针 【5】指针修饰 【6】大小端 【7】初见二级指针 练习&#xff1a; 【1】冒泡排序&#xff08;从小到大&#xff09; #include <stdio.h> //数组哪里的\0?自己和字符串…

Flink 在新能源场站运维的应用

摘要&#xff1a;本文整理自中南电力设计院工程师、注册测绘师姚远&#xff0c;在 Flink Forward Asia 2022 行业案例专场的分享。本篇内容主要分为四个部分&#xff1a; 建设背景 技术架构 应用落地 后续及其他 点击查看原文视频 & 演讲PPT 一、建设背景 建设背景主要…

Yalmip入门教程(3)-约束条件的定义

博客中所有内容均来源于自己学习过程中积累的经验以及对yalmip官方文档的翻译&#xff1a;https://yalmip.github.io/tutorials/ 之前的博客简单介绍了约束条件的定义方法&#xff0c;接下来将对其进行详细介绍。 首先简单复习一下&#xff1a; 1.定义约束条件可以使用矩阵拼接…

GRE和MGRE

目录 GRE GRE环境的搭建 MGRE MGRE的配置 MGRE环境下的RIP网络 MGRE实验 VPN 说到GRE&#xff0c;我们先来说个大家熟悉一点的&#xff0c;那就是VPN技术。 背景需求 企业、组织、商家等对专用网有强大的需求。 高性能、高速度和高安全性是专用网明显的优势。 物理专…

Notepad++ 配置python虚拟环境(Anaconda)

Notepad配置python运行环境步骤&#xff1a; 打开Notepad ->”运行”菜单->”运行”按钮在弹出的窗口内输入以下命令&#xff1a; 我的conda中存在虚拟环境 (1) base (2) pytorch_gpu 添加base环境至Notepad中 cmd /k chdir /d $(CURRENT_DIRECTORY) & call cond…