接口服务主要由两部分组成,即参数(输入)部分,响应(输出)部分。其中在SpringBoot中主要是Controller层作为API的开发处,其实在架构层面来讲,Controller本身是一个最高的应用层,它的职责是调用、组装下层的interface服务数据,核心是组装和调用,不应该掺杂其他相关的逻辑
博主最近在做一个数据服务的项目,而这个数据服务的核心就是对外暴露的API,值得高兴的这是一个从0开始的项目,所以终于不用受制于“某些历史”因素去续写各种风格的Controller,可以在项目伊始就以规范的技术和统一形式去搭建API。借此机会,梳理和汇总一下基于SpringBoot项目开发REST API的技术点和规范点。
接口服务主要由两部分组成,即参数(输入)部分,响应(输出)部分。其中在SpringBoot中主要是Controller层作为API的开发处,其实在架构层面来讲, Controller 本身是一个最高的应用层,它的职责是调用、组装下层的interface服务数据,核心是组装和调用,不应该掺杂其他相关的逻辑。
但是往往很多项目里针对Controller部分的代码都是十分混乱,有的 Controller 兼顾各种if else的参数校验,有的甚至直接在Controller进行业务代码编写;对于 Controller 的输出,有的粗略的加个外包装,有的甚至直接把service层的结构直接丢出去;对于异常的处理也是各种各样。
以上对于 Controller 相关的问题,这里统一用一系列 Controller 的封装处理来提供优化思路。优雅且规范的开发REST API需要做以下几步:
直接来看@RestController源码
@RestController注解等价于@Controller和@@ResponseBody,@ResponseBody注解的作用是告诉Spring MVC框架,该方法的返回值应该直接写入HTTP响应体中,而不是返回一个视图(View)。当一个控制器方法被标记为
@ResponseBody
时,Spring MVC会将方法的返回值序列化成JSON或XML等格式,然后发送给客户端。更适用于REST API的构建。
所以针对Controller接口的开发, 直接使用@RestController为好。它会自动将Controller下的方法返回内容转为REST API的形式 。
例如:
对于API来讲,一般是对外服务的基础,不能随意变更,但是随着需求和业务不断变化,接口和参数也会发生相应的变化。此时尽可能保证“开闭原则”,以新增接口或增强接口功能来支撑,此时就需要对API的版本进行维护,以版本号来确定同一接口的不同能力,一般版本都基于url来控制
例如:
http://localhost:8080/dataserver/v1/queryAccount
http://localhost:8080/dataserver/v2/queryAccount:相比v1版本增强了参数查询的灵活性
进行API版本控制主要分三步:
该注解可直接使用在Controller类上
首先定义一个条件匹配类,对应解析Url中的version与ApiVersion注解
这里补充一下 RequestCondition
相关概念: 它是 Spring 框架中用于请求映射处理的一部分。在 Spring MVC 中,
RequestCondition
接口允许开发者定义自定义的请求匹配逻辑,这可以基于请求的任何属性,例如路径、参数、HTTP 方法、头部等。相关的应用场景包括:
路径匹配(Path Matching) :使用
PatternsRequestCondition
来定义请求的路径模式,支持 Ant 风格的路径模式匹配,如/api/*
可以匹配所有/api
开头的请求路径 。请求方法匹配(Request Method Matching) :通过
RequestMethodsRequestCondition
来限制请求的 HTTP 方法,例如只允许 GET 或 POST 请求 。请求参数匹配(Request Params Matching) :使用
ParamsRequestCondition
来定义请求必须包含的参数,例如某些接口可能需要特定的查询参数才能访问 。请求头匹配(Request Headers Matching) :
HeadersRequestCondition
允许定义请求头的条件,例如某些接口可能需要特定的认证头部才能访问 。消费媒体类型匹配(Consumes Media Type Matching) :
ConsumesRequestCondition
用来定义控制器方法能够处理的请求体媒体类型,通常用于 RESTful API 中,例如只处理application/json
类型的请求体 。产生媒体类型匹配(Produces Media Type Matching) :
ProducesRequestCondition
定义了控制器方法能够返回的媒体类型,这通常与Accept
请求头结合使用以确定响应的格式 。自定义条件匹配 :开发者可以通过实现
RequestCondition
接口来定义自己的匹配逻辑,例如根据请求中的版本号来路由到不同版本的 API,实现 API 的版本控制 。组合条件匹配(Composite Conditions Matching) :在某些情况下,可能需要根据多个条件来匹配请求,
CompositeRequestCondition
可以将多个RequestCondition
组合成一个条件来进行匹配 。请求映射的优先级选择(Priority Selection for Request Mapping) :当存在多个匹配的处理器方法时,
RequestCondition
的compareTo
方法用于确定哪个条件具有更高的优先级,以选择最合适的处理器方法 。
创建一个版本映射处理器,使用
ApiVersionCondition
作为自定义条件来处理请求映射。当 Spring MVC 处理请求时,它会使用这个自定义的映射处理器来确定哪个版本的 API 应该处理请求。
将上述的处理器注册到SpringMvc的处理流程中
验证:
针对test接口进行不同版本的请求:
针对Account扩展版本调用上一版本接口
当请求对应的版本不存在接口时,会匹配之前版本的接口,即请求
/v2/account/extend
接口时,由于v2 控制器未实现该接口,所以自动匹配v1 版本中的接口。这就实现了
API版本继承
。
@Validated
是一个用于 Java 应用程序中的注解,特别是在 Spring 框架中,以指示目标对象或方法需要进行验证。这个注解通常与 JSR 303/JSR 380 规范的 Bean Validation API 结合使用,以确保数据的合法性和完整性。
@Validated注解的三种用法:
方法级别验证
:当
@Validated
注解用在方法上时,它指示 Spring 在调用该方法之前执行参数的验证。如果参数不符合指定的验证条件,将抛出
MethodArgumentNotValidException
。
类级别验证
:将
@Validated
注解用在类上,表示该类的所有处理请求的方法都会进行验证。这可以减少在每个方法上重复注解的需要。
组合注解
:Spring 还提供了
@Valid
注解,它是
@Validated
的一个更简单的形式,只触发验证并不指定特定的验证组(Validation Groups)。
@Validated
允许你指定一个或多个验证组,这在需要根据不同情况执行不同验证规则时非常有用。
在REST API中进行参数验证一般使用方法级别验证即可,即对参数Dto的类内信息进行验证,例如一个分页的查询参数类:
在Controller中配合@Validated使用:
此时如果前端传入参数不合法,例如pageNo为0又或者productType不存在,则会抛出
MethodArgumentNotValidException
的异常。稍后对于异常进行处理即可完成参数的验证。
这里的@Max
、
@Min
和
@NotNull
注解属于 Bean Validation API 的一部分,这是一个 JSR 303/JSR 380 规范,用于在 Java 应用程序中提供声明式验证功能。这些注解用于约束字段值的范围和非空性。类似的注解还有:
注解 | 作用 |
@NotNull |
验证注解的字段值不能为
null
。
|
@NotEmpty |
与
@NotNull
类似,但用于集合或字符串,验证注解的字段值不能为
null
,且对于字符串,长度不能为 0。
|
@NotBlank |
验证注解的字段值不能为
null
,且不能是空白字符串(空白包括空格、制表符等)。
|
@Min(value) |
验证注解的字段值是否大于或等于指定的最小值。
value
参数接受一个整数。
|
@Max(value) |
验证注解的字段值是否小于或等于指定的最大值。
value
参数接受一个整数。
|
@Size(min, max) | 验证字符串或集合的大小在指定的最小值和最大值之间。 |
@Pattern(regex) | 验证字段值是否符合指定的正则表达式。 |
注:SpringBoot 2.3.1 版本默认移除了校验功能,如果想要开启的话需要添加以上依赖
@RestControllerAdvice
是 @ResponseBody+@ControllerAdvice的集合注解,用于定义一个控制器级别的异常处理类。一般用来进行全局异常处理,在@RestControllerAdvice
类中处理异常后,可以直接返回一个对象,该对象会被转换为 JSON 或 XML 响应体,返回给客户端。
在
使用@Validated和 Bean Validation API 的注解进行参数校验后,当出现不符合规定的参数会抛出
MethodArgumentNotValidException
异常
,这里就可以使用@RestControllerAdvice注解来创建一个全局Controller异常拦截类,来统一处理各类异常
这里只以
MethodArgumentNotValidException
异常进行拦截,在
@RestControllerAdvice类内可以创建多个方法,通过@ExceptionHandler对不同的异常进行定制化处理,这样当Controller内发生异常,都可以在@RestControllerAdvice类内进行截获、处理、返回给客户端安全的信息。
首先,进行统一的响应格式,这里需要封装一个固定返回格式的结构对象: ResponseData
其中对于相关的状态码最好进行统一的封装,便于以后的开发,创建状态枚举:
将上述的 ResponseData 中状态类相关替换为枚举
这样Controller的接口统一返回格式就是标准的结构了。
有了统一响应体的Controller在返回时可以这样写:
即便如此,团队开发中可能还会出现换个人新写Controller不知道有统一返回体这回事,为了更保险,可以通过AOP进行统一对结果进行封装,不论Controller返回啥,到客户端的数据都包含一个包装体。
具体实现是使用 @RestControllerAdvice类 实现 ResponseBodyAdvice 接口来完成。
我们以test接口为例,test接口原本返回的是String,而toint返回的是Integer
但是页面返回是JSON字符串和返回体:
接口开发完成,调试时,大多数都是使用Postman模拟请求调试或者直接用前端代码调用调试,其实这两种都比较麻烦,尤其面对复制参数时,Postman要逐个接口的录入,十分费事,其实这里可以在SpringBoot中引入Swagger接口文档组件,接口文档和接口调试一并解决。
直接在maven中引入相关依赖:
标准的swagger3引入以上两个依赖即可,相关版本可自行选择
下面进行swagger的配置类和一些swagger相关页面的配置
Swagger相关注解明细
注解 | 使用位置 | 作用 |
@Api | 作用在类上, Controller 类 |
表示对类的说明,通常用于描述 Controller 的作用和分类,如
@Api(tags = "用户管理"),后续会在Swagger文档中以
目录形式
展示
|
@ApiOperation | 作用在方法上,一般为 Controller中具体方法 |
描述 API 接口的具体操作和功能,例如
@ApiOperation(value = "获取用户列表", notes = "根据条件获取用户列表")
,在swagger文档中以目录内容体现
|
@ApiModel | 作用于类上,一般是 参数实体类 |
表示这是一个模型类,通常与
@ApiModelProperty
结合使用来描述模型属性 。
|
@ApiModelProperty | 用于模型类的属性上, 参数类的成员变量 |
描述属性的信息,如
@ApiModelProperty(value = "用户名", required = true)
|
@ApiImplicitParams 和 @ApiImplicitParam | 用于方法上,一般为 Controller中具体方法 | 描述接口的隐含参数,例如请求参数或请求头信息 |
@ApiResponses 和 @ApiResponse | 用于方法上,一般为 Controller中具体方法 | 描述接口的响应信息,可以指定不同的响应状态码和对应的描述信息 。 |
@ApiIgnore | 用于类或方法上 | 表示忽略该类或方法,不将其显示在Swagger文档中。 |
@Api 和 @ApiOperation 使用
@ApiMode 和 @ApiModelProperty
@ApiImplicitParams 和 @ApiImplicitParam
与ApiMode和ApiModeProperty功能一致,一般用于get请求中的参数描述
使用swagger后,直接在页面访问 http://127.0.0.1:8080/XXX/doc.html即可访问接口页面
不要复杂的postman调用,本地调试可以直接使用调试功能
关于参数验证的异常处理和统一返回结构,可以使用一个类来完成,以下是完整模板:
真实飞行员模拟 中文版 1.0.4 91.17 MB
下载
湘ICP备2022002427号-10湘公网安备:43070202000427号
© 2013~2019 haote.com 好特网