SpringBoot + SpringCloudGateway + Swagger + knife4j
1.什么是OpenAPI
随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了:前端渲染、前后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。 前端和后端的唯一联系,变成了API接口;API文档变成了前后端开发人员联系的纽带,变得越来越重要。
没有API文档工具之前,大家都是手写API文档的,在什么地方书写的都有,而且API文档没有统一规范和格式,每个公司都不一样。这无疑给开发带来了灾难。
OpenAPI规范(OpenAPI Specification 简称OAS)是Linux基金会的一个项目,试图通过定义一种用来描述API格式或API定义的语言,来规范RESTful服务开发过程。目前V3.0版本的OpenAPI规范已经发布并开源在github上 。
2.swagger是什么
Swagger就是一个实现了OpenAPI规范的工具集
Swagger 是一个规范且完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务
Swagger 的目标是对 REST API 定义一个标准且和语言无关的接口,可以让人和计算机拥有无须访问源码、文档或网络流量监测就可以发现和理解服务的能力
当通过 Swagger 进行正确定义,用户可以理解远程服务并使用最少实现逻辑与远程服务进行交互。与为底层编程所实现的接口类似,Swagger 消除了调用服务时可能会有的猜测
官网:https://swagger.io/
3.Swagger有什么优势
-
支持 API 自动生成同步的在线文档
使用Swagger之后可以直接通过代码生成文档,无须开发者手动编写文档
-
提供 Web 页面在线测试 API
Swagger 生成的文档还支持在线测试。参数和格式确定,输入对应的值即可在线测试接口
4.如何集成
4.1SpringBoot集成Swagger
SpringBoot已经集成了Swagger,使用简单注解即可生成swagger的API文档。
4.1.1 引入依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
4.1.2 编写配置类
/**
* swagger的配置
* @author shengshenglalala
* @date 2021/8/26 9:10
*/
@Configuration
/**
* @EnableSwagger2 指定开启swagger文档
*/
@EnableSwagger2
public class SwaggerConfig {
/**
* 一个文档分组 有多个分组就可以指定多个Docket
*/
@Bean
public Docket allApi() {
//分组名称可以随便定义
String groupName = "RestfulApi";
return new Docket(DocumentationType.SWAGGER_2)
.groupName(groupName)
.genericModelSubstitutes(ResponseEntity.class)
.useDefaultResponseMessages(true)
.forCodeGeneration(false)
.select()
//指定扫描规则 这里可以根据url正则匹配 可以指定包名以及其他的规则
.paths(Predicates.not(PathSelectors.regex("/actuator.*")))
.build()
.apiInfo(new ApiInfoBuilder()
.title("swagger-backend项目所有接口")
.description("swagger-backend项目所有接口")
.termsOfServiceUrl("")
.version("1.0.0")
.build());
}
}
4.1.3 接口声明
@RestController
@Api(tags = "用户相关")
@RequestMapping(value = "/backend")
public class UserController {
@Resource
private UserService userService;
@ApiOperation(value = "用户查询",notes = "查询所有用户")
@PostMapping(value ="/findAllUser")
public Result<List<User>> findAllUser(User user){
List<User> userList = userService.findAllUser(user);
return Result.of(200, "success",userList);
}
}
4.1.4 测试
启动backend项目 访问 http://localhost:8002/swagger-ui.html
4.2.常用注解
/**
@Api:修饰整个类,描述Controller的作用
@ApiOperation:描述一个类的一个方法,或者说一个接口
@ApiParam:单个参数描述
@ApiModel:用对象来接收参数
@ApiProperty:用对象接收参数时,描述对象的一个字段
@ApiResponse:HTTP响应其中1个描述
@ApiResponses:HTTP响应整体描述
@ApiIgnore:使用该注解忽略这个API
@ApiError :发生错误返回的信息
@ApiImplicitParam:一个请求参数
@ApiImplicitParams:多个请求参数
*/
4.2.1controller层相关注解
/**
* description:
*
* @author shengshenglalala
* @date 2021/8/24 12:23
*/
@RestController
/**
* @Api 修饰整个类,描述Controller的作用
* tags 接口说明,可以在页面中显示。是数组,可以配置多个,当配置多个的时候,在页面中会显示多个接口的信息
* value 该参数没什么意义,在UI界面上不显示,可以不用配置
* description 用于描述用户基本信息操作
*/
@Api(tags = {"用户相关"})
@RequestMapping(value = "/backend")
public class UserController {
@Resource
private UserService userService;
/**
* @ApiOperation 用于方法,描述方法的作用,每一个接口的定义
* value="方法的用途和作用"
* notes="方法的注意事项和备注"
* tags:说明该方法的作用,参数是个数组,可以填多个。
* 格式:tags={"作用1","作用2"}
* (在这里建议不使用这个参数,会使界面看上去有点乱,前两个常用)
*/
@ApiOperation(value = "用户查询",notes = "查询所有用户")
@PostMapping(value ="/findAllUser")
public Result<List<User>> findAllUser(){
List<User> userList = userService.findAllUser();
return Result.of(200, "success",userList);
}
/**
*@ApiParam 用于 Controller 中方法的参数说明
* value:参数说明
* required是否必须
* name描述参数名称
* defaultValue默认值
* allowableValues 允许的参数范围
*/
@ApiOperation(value = "用户查询",notes = "根据id查询")
@GetMapping(value ="/findById/{id}")
/**
* @ApiImplicitParams 用在方法上,组装了多个@ApiImplicitParam
* @ApiImplicitParam 描述具体某个参数的信息
* name 该参数的字段名称
* value 描述改字段的含义
* dataType 描述传参类型
* required 是否必须
* defaultValue 默认值
*/
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "用户ID", dataType = "Long", required = true, defaultValue = "1") })
/**
* @ApiResponses HTTP响应整体描述 组装了多个 @ApiResponse
* @ApiResponse 用于方法上,说明接口响应的一些信息
* code 响应码
* message 响应消息
* response 响应体
*/
@ApiResponses({ @ApiResponse(code = 200, message = "success", response = Result.class),
@ApiResponse(code = 500, message = "failure", response = Result.class)})
public Result<User> findById(@ApiParam(value = "用户id", required = true,name = "id",defaultValue = "1",allowableValues = "range[1, 5]") @PathVariable(value = "id")Long id){
return Result.of(200, "success",userService.findById(id));
}
}
4.2.2model相关注解
/**
* description:
*
* @author shengshenglalala
* @date 2021/8/24 12:20
*/
/**
* @ApiModel 用于响应实体类上,用于说明实体作用
* description="描述实体的作用"
* value="给该类取一个替代名称"
*/
@ApiModel(value = "user")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User implements Serializable {
/**
* @ApiModelProperty 用在属性上,描述实体类的属性
* value="用户名" 描述参数的意义
* name="name" 参数的变量名
* required=true 参数是否必选
* example="0" 参数可以传递的值的举例
* allowableValues= range[1, 5], range(1, 5), range[1, 5),range[1, infinity] 允许传递的值的范围
* hidden=true 描述该字段在swagger文档中是否展示
*/
@ApiModelProperty(value = "主键id" , example = "0")
private Long id;
@ApiModelProperty(value = "用户名" , example = "张三")
private String name;
@ApiModelProperty(value = "生日" , example = "2021-08-15 00:00:00")
private Date birthday;
}
4.3gateway的集成
该项目使用的是spring-cloud-starter-gateway + eureka注册中心
4.3.1引入依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
4.3.2编写配置类
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket docket() {
// basePackage 需要扫描注解生成文档的路径
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.shengsheng.controller"))
.paths(PathSelectors.any())
.build();
}
public ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Swagger Entrance provided by GATEWAY")
.description("这是展示通过网关聚合所有子模块的swagger文档的例子")
.version("1.0").build();
}
}
4.3.3 实现SwaggerResourcesProvider类用于微服务的自动发现
/**
* @author shengshenglalala
*/
@Component
@Primary
public class GatewaySwaggerResourcesProvider implements SwaggerResourcesProvider {
@Resource
private RouteLocator routeLocator;
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* 给各个微服务进行重命名,以便在文档中看到更人性化的名称
*/
private static final Map<String,String> INDEX_MAP = new HashMap<>();
static {
INDEX_MAP.put("swagger-api","01-swagger-api");
INDEX_MAP.put("swagger-backend","02-swagger-backend");
}
/**
* 重写get方法添加swagger的数据源
*/
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<Route> routes = routeLocator.getRoutes();
//通过RouteLocator获取路由配置,遍历获取所配置服务的接口文档,这样不需要手动添加,实现动态获取
for (Route route : routes) {
//微服务名称
String routeId = route.getId();
logger.info("routeId:{}",routeId);
String fullPath = route.getFullPath();
logger.info("fullPath:{}",fullPath);
String newName = INDEX_MAP.get(routeId.toLowerCase());
//这里用于过滤我们不想展示的微服务
// 比如假设我们有个只给后台开发人员使用的微服务这个微服务不想给前端看到就可以在这里过滤掉
if(!StringUtils.isEmpty(newName)){
resources.add(swaggerResource(newName, fullPath.replace("**", "v2/api-docs?group=")));
}
}
resources.sort(Comparator.comparing(SwaggerResource::getName));
return resources;
}
/**
* 这个地方是结合分组的实际情况,前面配置中groupName是写死了RestfulApi,这里就偷个懒
* @param name 分组名称
*/
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location + "RestfulApi");
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
4.3.4 项目结构
- swagger-api:api的微服务
- swagger-backend:backend的微服务
- swagger-gateway:网关微服务
- swagger-registry:注册中心微服务
4.3.5 测试
启动网关项目 访问 http://localhost:10010/swagger-ui.html
4.4集成knife4j
4.4.1引入依赖
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-ui</artifactId>
<version>2.0.7</version>
</dependency>
4.4.2 测试
启动网关项目 访问 http://localhost:10010/doc.html