springboot/web项目优秀的后端接口体系,看一篇就够了

Erin ·
更新时间:2024-09-21
· 962 次阅读

springboot/web项目优秀的后端接口体系,看一篇就够了

项目构建-统一参数校验,统一结果响应,统一异常处理,统一错误处理,统一日志记录,统一生成api文档

1. 前言

一个后端接口大致分为四个部分组成:接口地址(url)、接口请求方式(get、post等)、请求数据(request)、响应数据(response)。
本文主要演示如何构建起一个优秀的后端接口体系,体系构建好了自然就有了规范,同时再构建新的后端接口也会十分轻松。

2. 所需依赖包

这里用的是SpringBoot配置项目,本文讲解的重点是后端接口,所以只需要导入一个spring-boot-starter-web包就可以了:

pom.xml:

4.0.0 org.springframework.boot spring-boot-starter-parent 2.2.4.RELEASE com.rudecrab validation-and-exception-handler 0.0.1-SNAPSHOT validation-and-exception-handler Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-web com.github.xiaoymin knife4j-spring-boot-starter 2.0.1 org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine org.springframework.boot spring-boot-maven-plugin

本文还用了swagger来生成API文档,lombok来简化类,logback来生成日志。都不是必须的,可用可不用。

3.统一参数校验

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

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

public String addUser(User user) { if (user == null || user.getId() == null || user.getAccount() == null || user.getPassword() == null || user.getEmail() == null) { return "对象或者对象字段不能为空"; } if (StringUtils.isEmpty(user.getAccount()) || StringUtils.isEmpty(user.getPassword()) || StringUtils.isEmpty(user.getEmail())) { return "不能输入空字符串"; } if (user.getAccount().length() 11) { return "账号长度必须是6-11个字符"; } if (user.getPassword().length() 16) { return "密码长度必须是6-16个字符"; } if (!Pattern.matches("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$", user.getEmail())) { return "邮箱格式不正确"; } // 参数校验完毕后这里就写上业务逻辑 return "success"; }

这还没有进行业务操作呢光是一个参数校验就已经这么多行代码,实在不够优雅。

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

3.1 Validator + BindResult进行校验

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

@Data public class User { @NotNull(message = "用户id不能为空") private Long id; @NotNull(message = "用户账号不能为空") @Size(min = 6, max = 11, message = "账号长度必须是6-11个字符") private String account; @NotNull(message = "用户密码不能为空") @Size(min = 6, max = 11, message = "密码长度必须是6-16个字符") private String password; @NotNull(message = "用户邮箱不能为空") @Email(message = "邮箱格式不正确") private String email; }

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

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

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

public String addUser(User user) { // 直接编写业务逻辑 return "success"; }

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

{ "account": "12345678", "email": "123@qq.com", "id": 0, "password": "123" }

再来看一下接口的响应数据:

在这里插入图片描述

使用Validator+ BindingResult已经是非常方便实用的参数校验方式了,在实际开发中也有很多项目就是这么做的,不过这样还是不太方便,因为你每写一个接口都要添加一个BindingResult参数,然后再提取错误信息返回给前端。这样有点麻烦,并且重复代码很多(尽管可以将这个重复代码封装成方法)。

我们能否去掉BindingResult这一步呢?当然是可以的!

3.2 Validator + 自动抛出异常

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

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

去掉之后会发生什么事情呢?直接来试验一下,还是按照之前一样故意传递一个不符合校验规则的参数给接口。

此时我们观察控制台可以发现接口引发MethodArgumentNotValidException异常:在这里插入图片描述

异常是引发了,可我们并没有编写返回错误信息的代码呀,那参数校验失败了会响应什么数据给前端呢? 我们来看一下刚才异常发生后接口响应的数据:
${LOG_PATTERN} ${LOG_HOME}/all_${LOG_PREFIX}.log ${LOG_DIR}/all_${LOG_PREFIX}%d{yyyy-MM-dd}.%i.log ${MAX_HISTORY} ${MAX_FILE_SIZE} ${LOG_PATTERN} ${LOG_HOME}/err_${LOG_PREFIX}.log ${LOG_DIR}/err_${LOG_PREFIX}%d{yyyy-MM-dd}.%i.log ${MAX_HISTORY} ${MAX_FILE_SIZE} ${LOG_PATTERN} <!----> <!----> <!----> 9.2 application.yml:

在yml配置里面规定相关的输出路径:

logging: config: classpath:./logback-spring.xml level: com.rudecrab.demo.controller: WARN path: ./logs 9.3 测试生成日志文件

然后在需要的方法中使用 @Slf4j注解,和log对象来输出日志信息。

import lombok.extern.slf4j.Slf4j; @Slf4j public class UserController { @PostMapping("/addUser") public String addUser(@RequestBody @Valid User user) { //日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出。 log.trace("日志输出 trace"); log.debug("日志输出 debug"); log.info("日志输出 info"); log.warn("日志输出 warn"); log.error("日志输出 error"); return userService.addUser(user); } }

如图:
在这里插入图片描述
以后项目运行,会在当前项目的./logs文件夹下生成日志文件。
在这里插入图片描述

10. knife4j生成API文档

knife4j 是为Java MVC框架集成Swagger 生成Api文档的增强解决方案,前身是swagger-bootstrap-ui。

所以我们推荐采用了swagger的新增强版knife4j来生成API接口文档。
knife4j的使用方法和swagger几乎一模一样。

10.1 pom.xml 引入依赖

pom.xml 引入依赖:

com.github.xiaoymin swagger-bootstrap-ui 1.9.6 com.github.xiaoymin knife4j-spring-boot-starter 2.0.1

在这里我用的新版本的依赖。

10.2 新建SwaggerConfig类:

springboot的配置文件和启动类不用做任何特殊配置,使用knife4j需要一个swagger的配置类。

knife4j/swagger的配置类:

package com.rudecrab.demo.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** * 使用了其新版名称为:knife4j * @description swagger接口文档配置类 */ @Configuration @EnableSwagger2 public class SwaggerConfig { /** * 是否启用swagger文档 * enable 开启 disenable 关闭 * 默认访问地址:http://${yourhost}:${yourport}/doc.html */ @Value("${swagger.enable}") private boolean enable; @Bean public Docket docket(){ return new Docket(DocumentationType.SWAGGER_2) .enable(enable) .apiInfo(apiInfo()) .select() // 这里配置要扫描的包,接口在哪个包就配置哪个包 .apis(RequestHandlerSelectors.basePackage("com.rudecrab.demo.controller")) .paths(PathSelectors.any()) .build(); } public ApiInfo apiInfo(){ return new ApiInfoBuilder() .title("参数校验和统一异常处理Demo") .description("用来演示参数校验和统一异常处理") .termsOfServiceUrl("zoutao.info") .contact(new Contact("ZouTao", "", "zoutao6666@qq.com")) .version("1.0") .build(); } }

可以看到,内容上和以前的swagger配置类没什么变化,唯一的变化是类注解需要比原来的swagger多加一个 @EnableSwaggerBootstrapUi。这样knife4j的所有配置都完成了。

以上有两个注解需要特别说明,如下:

@EnableSwagger2
该注解是Springfox-swagger框架提供的使用Swagger注解,该注解必须加

@EnableKnife4j
该注解是knife4j提供的增强注解,Ui提供了例如动态参数、参数过滤、接口排序等增强功能,如果你想使用这些增强功能就必须加该注解,否则可以不用加

10.3 添加对应的API信息:

在这里插入图片描述

swagger常用注解:

Api Api 用在类上,说明该类的作用。 ApiModel 描述一个Model的信息 ApiModelProperty 描述一个model的属性。 ApiOperation 用在方法上,说明方法的作用, ApiParam 请求属性 ApiResponse 响应配置 ApiResponses 响应集配置 ResponseHeader 响应头设置

具体可参考:swagger常用注解说明。

10.4 查看生成的接口文档:

默认访问地址:http://host:{host}:host:{port}/doc.html

运行springboot项目,自动生成接口文档,
访问地址: http://localhost:8080/doc.html

在这里插入图片描述
还可以在其中进行接口调试:
在这里插入图片描述
更多功能参考 knife4j官网。

11. 总结

自此整个后端接口基本体系就构建完毕了

通过Validator+自动抛出异常来完成了方便的参数校验 通过全局异常处理 + 自定义异常完成了异常操作的规范 通过统一结果响应完成了对响应数据的返回规范 统一错误处理完成了不同端发出的请求的返回。 通过logback统一日志的输出方式,进行规范记录 通过knife4j统一生成API文档,方便API接口说明。

代码地址晚些会给出,如果觉得不错或者帮助到你,希望大家给个Star。

参考地址:
https://juejin.im/post/5e7ab0bae51d45271b749815#heading-13
https://juejin.im/post/5e073980f265da33f8653f2e#heading-2
https://segmentfault.com/a/1190000019795918
https://www.toutiao.com/a6755002314790011399/
https://www.cnblogs.com/zxg-6/p/12629821.html


作者:csdnzoutao



接口 boot ng pr spring springboot 后端接口 Web gb

需要 登录 后方可回复, 如果你还没有账号请 注册新账号