知方号

知方号

SpringBoot使用 SpringBoot 手把手教你打出优雅的后端接口

前言

一个后端接口大致分为四个部分组成:接口地址(url)、接口请求方式(get、post等)、请求数据(request)、响应数据(response)。如何构建这几个部分每个公司要求都不同,没有什么“一定是最好的”标准,但一个优秀的后端接口和一个糟糕的后端接口对比起来差异还是蛮大的,其中最重要的关键点就是看是否规范!

此文就一步一步演示如何构建起一个优秀的后端接口体系,体系构建好了自然就有了规范,同时再构建新的后端接口也会十分轻松。

开发环境

IDEA-2023.2.4MAVEN-3.6SpringBoot-2.1.1.RELEASE

引入 POM 依赖:

新建一个 SpringBoot 工程,由于重点是讲解后端接口,所以只需要导入一个spring-boot-starter-web 包就可以了。

org.springframework.bootspring-boot-starter-web

需要统一做的步骤

需要统一做的步骤:

统一入参校验:Validator + 自动抛出异常统一异常处理:统一异常处理 + 自定义异常统一结果返回:统一响应体统一状态码:统一枚举类统一日志:logback

统一入参校验

一个接口一般对参数(请求数据)都会进行安全校验,参数校验的重要性自然不必多说,那么如何对参数进行校验就有讲究了。

传统的业务层校验

首先我们来看一下最常见的做法,就是在业务层进行参数校验:

public String addUser(UserVo userVo) { if (null == userVo || null == userVo.getName() || null == userVo.getTelPhone()) { return "对象或对象字段不能为空"; } if (StringUtils.isEmpty(userVo.getName()) || StringUtils.isEmpty(userVo.getTelPhone())) { return "不能输入空字符串"; } if (!RegularUtil.isLegalTelPhone(userVo.getTelPhone())) { return "输入的手机号不合法"; }// 参数校验完毕后这里就写上业务逻辑 return "success";}

这样做当然是没有什么错的,而且格式排版整齐也一目了然,不过这样太繁琐了,这还没有进行业务操作呢。仅是一个参数校验就已经这么多行代码,实在不够优雅。

我们来改进一下,使用 Spring Validator 和 Hibernate Validator 这两套 Validator 来进行方便的参数校验!这两套 Validator 依赖包已经包含在前面所说的 web 依赖包里了,所以可以直接使用。

Validator + BindResult 进行校验

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

@Datapublic class UserVo { // 主键 @NotNull(message = "用户id不能为空") private String id; // 名称 @NotNull(message = "用户名称不能为空") @Size(min = 6, max = 11, message = "名称长度必须是6-11个字符") private String name; // 联系方式 @NotNull(message = "联系方式不能为空") private String telPhone;}

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

@RestController@RequestMapping("/user")public class UserController { @Autowired private UserService userService; // 添加用户信息 @PostMapping("/addUser") public String addUser(@RequestBody @Valid UserVo userVo, BindingResult bindingResult) { // 如果有参数校验失败,会将错误信息封装成对象组装在BindingResult里 for (ObjectError objectError : bindingResult.getAllErrors()) { return objectError.getDefaultMessage(); } return userService.addUser(userVo); }}

这样,当请求数据传递到接口的时候,Validator 就自动完成校验了,校验的结果就会封装到 BindingResult 中去。如果有错误信息,我们就直接返回给前端,业务逻辑代码也根本没有执行下去。

此时,业务层里的校验代码就已经不需要了,直接进行逻辑处理:

@Overridepublic String addUser(UserVo userVo) { return "success";}

现在可以看一下参数校验效果。我们故意给这个接口传递一个不符合校验规则的参数,先传递一个错误数据给接口,故意将 sName 这个字段不满足校验条件:

{ "sId": "1", "sName": "zzc", "sTelPhone": "13217"}

再来看一下接口的响应数据: 这样是不是方便很多?不难看出使用Validator校验有如下几个好处:

简化代码,之前业务层那么一大段校验代码都被省略掉了使用方便,那么多校验规则可以轻而易举的实现减少耦合度,使用Validator能够让业务层只关注业务逻辑,从基本的参数校验逻辑中脱离出来

使用 Validator+ BindingResult 已经是非常方便实用的参数校验方式了,在实际开发中也有很多项目就是这么做的,不过这样还是不太方便,因为你每写一个接口都要添加一个 BindingResult参数,然后再提取错误信息返回给前端。

这样有点麻烦,并且重复代码很多(尽管可以将这个重复代码封装成方法)。我们能否去掉 BindingResult这一步呢?当然是可以的!

Validator + 自动抛出异常

我们完全可以将BindingResult这一步给去掉:

@PostMapping("/addUser")public String addUser(@RequestBody @Valid UserVo userVo) { return userService.addUser(userVo);}

去掉之后会发生什么事情呢?直接来试验一下,还是按照之前一样故意传递一个不符合校验规则的参数给接口。此时我们观察控制台可以发现接口已经引发 MethodArgumentNotValidException 异常了: 其实,这样就已经达到我们想要的效果了,参数校验不通过自然就不执行接下来的业务逻辑,去掉 BindingResult 后会自动引发异常。异常发生了后,自然而然地就不会执行业务逻辑。也就是说,我们完全没必要添加相关 BindingResult 相关操作。

不过事情还没有完,异常是引发了,可我们并没有编写返回错误信息的代码呀,那参数校验失败了会响应什么数据给前端呢?

我们来看一下刚才异常发生后接口响应的数据: 没错,是直接将整个错误对象相关信息都响应给前端了!这样就很难受,不过解决这个问题也很简单,就是我们接下来要讲的全局异常处理!

统一异常处理

参数校验失败会自动引发异常,我们当然不可能再去手动捕捉异常进行处理,不然还不如用之前 BindingResult 方式呢。

又不想手动捕捉这个异常,又要对这个异常进行处理,那正好使用SpringBoot 全局异常处理 来达到一劳永逸的效果!

基本使用

首先,我们需要新建一个类,在这个类上加上 @ControllerAdvice或@RestControllerAdvice 注解,这个类就配置成全局处理类了。(这个根据你的 Controller 层用的是 @Controller 还是 @RestController 来决定)

然后在类中新建方法,在方法上加上 @ExceptionHandler 注解并指定你想处理的异常类型,接着在方法内编写对该异常的操作逻辑,就完成了对该异常的全局处理!

我们现在就来演示一下对参数校验失败抛出的 MethodArgumentNotValidException 全局处理:

@RestControllerAdvicepublic class UserExceptionControllerAdvice { // 用户类入参校验异常 @ExceptionHandler(MethodArgumentNotValidException.class) public String MethodArgumentNotValidException(MethodArgumentNotValidException exception) { ObjectError objectError = exception.getBindingResult().getAllErrors().get(0); return objectError.getDefaultMessage(); }}

我们再来看下这次校验失败后的响应数据: 没错,这次返回的就是我们制定的错误提示信息!

我们通过全局异常处理优雅的实现了我们想要的功能!以后我们再想写接口参数校验,就只需要在入参的成员变量上加上 Validator 校验规则注解,然后在参数上加上 @Valid 注解即可完成校验,校验失败会自动返回错误提示信息,无需任何其他代码!

自定义异常

全局处理当然不会只能处理一种异常,用途也不仅仅是对一个参数校验方式进行优化。

在很多情况下,我们需要手动抛出异常,比如在业务层当有些条件并不符合业务逻辑,我这时候就可以手动抛出异常从而触发事务回滚。那手动抛出异常最简单的方式就是 throw new RuntimeException("异常信息") 了,不过使用自定义会更好一些:

自定义异常可以携带

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lizi9903@foxmail.com举报,一经查实,本站将立刻删除。