「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」
基于微服务开发的大环境,有一个公共统一的接口文档很关键,所以借着此次整合项目组件的机会,分享下Knife4j + Nacos + springCloudGateway的整合过程,希望帮助到各位开发者,不废话,纯干货!!!
nacos的部署配置参考:Naocs 2.0版本变更要点详解
一、引入依赖
目前knife4j做的不错,全局配合springboot只需要引入一个依赖就可以了,无论是网关,还是其他的子服务,都需要引入如下的依赖:
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.9</version>
</dependency>
另外,因为我本人懒得写get/set方法,所以通常都会引入lombok:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
nacos注册中心和配置中心的依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
二、服务配置
2.1 网关-SpringCloudGateway
网关需要在springboot项目中引入其依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
【注意】 不要引入下面的依赖,否则网关启动会报冲突错误,而普通springboot服务是需要的:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置文件增加如下内容:
spring:
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
增加 如下两个class:
- SwaggerProvider:获取nacos当中注册的服务名称,以服务名称拼接接口文档路径,组成完成的接口文档访问路径
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.stereotype.Component; import springfox.documentation.swagger.web.SwaggerResource; import springfox.documentation.swagger.web.SwaggerResourcesProvider; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @Component public class SwaggerProvider implements SwaggerResourcesProvider { /** * 接口地址 */ public static final String API_URI = "/v2/api-docs"; /** * 路由加载器 */ @Autowired private RouteLocator routeLocator; /** * 网关应用名称 */ @Value("${spring.application.name}") private String applicationName; @Override public List<SwaggerResource> get() { //接口资源列表 List<SwaggerResource> resources = new ArrayList<>(); //服务名称列表 List<String> routeHosts = new ArrayList<>(); // 获取所有可用的应用名称 routeLocator.getRoutes().filter(route -> route.getUri().getHost() != null) .filter(route -> !applicationName.equals(route.getUri().getHost())) .subscribe(route -> routeHosts.add(route.getUri().getHost())); // 去重,多负载服务只添加一次 Set<String> existsServer = new HashSet<>(); routeHosts.forEach(host -> { // 拼接url String url = "/" + host + API_URI; //不存在则添加 if (!existsServer.contains(url)) { existsServer.add(url); SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setUrl(url); swaggerResource.setName(host); resources.add(swaggerResource); } }); return resources; } } - SwaggerHandler:获取接口文档
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; import springfox.documentation.swagger.web.*; import java.util.Optional; @RestController @RequestMapping("/swagger-resources") public class SwaggerHandler { /** * 权限配置,没有的不用关注 */ @Autowired(required = false) private SecurityConfiguration securityConfiguration; @Autowired(required = false) private UiConfiguration uiConfiguration; private final SwaggerResourcesProvider swaggerResources; @Autowired public SwaggerHandler(SwaggerResourcesProvider swaggerResources) { this.swaggerResources = swaggerResources; } @GetMapping("/configuration/security") public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK)); } @GetMapping("/configuration/ui") public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK)); } /** * 获取接口信息 * @date: 2020/12/3 * @param * @return reactor.core.publisher.Mono<org.springframework.http.ResponseEntity> * @author weirx * @version 3.0 */ @GetMapping("") public Mono<ResponseEntity> swaggerResources() { return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); } }
2.2 业务服务-用户服务为例
引入一个配置类,次配置类获取我们在服务配置文件当中配置的基本信息,加载到接口文档当中:
/**
* description: knife4j配置文件
*
* @author: weirx
* @time: 2022/1/18 9:44
*/
@Data
@Configuration
@EnableSwagger2WebMvc
@ConfigurationProperties(prefix="swagger")
public class Knife4jConfiguration {
private String version;
private String termsOfServiceUrl;
private String description;
private String basePackage;
@Bean(value = "defaultApi2")
public Docket defaultApi2() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
.description(description)
.termsOfServiceUrl(termsOfServiceUrl)
.version(version)
.build())
.select()
//这里指定Controller扫描包路径
.apis(RequestHandlerSelectors.basePackage(basePackage))
.paths(PathSelectors.any())
.build();
return docket;
}
}
这个配置类建议抽取一个公共依赖服务,或者自己封装一个starter,使用spi的方式加载,否则每个服务都需要增加相同的配置,依赖,甚至是通用工具类,枚举等等。就像spring boot为我们封装了很多引入依赖后就直接可以使用的starter一样。我们可以在其基础上再次封装,添加我们自己需要的配置。
本文使用的样例是使用starter的方式,包括一些公共依赖等。
不明白的同学可参考开篇的gitee源码地址。
配置文件中增加如下,其中base-package这个配置一定要将你的controller包含在内,否则是找不到你的接口地址的:
swagger:
enabled: true
base-package: com.wjbgn.user
version: V1.0
description: 用户服务
terms-of-service-url: http://localhost:8003/doc.html
用户服务controller中接口增加部分注解,用来展示接口信息,我们以之前集成mybatis-plus的接口为例,做如下修改,以下只以新增接口代码为例:
@Api:是整个controller展示的名字@ApiOperation:是接口的描述
/**
* @description: 用户controller
* @author:weirx
* @date:2022/1/17 17:39
* @version:3.0
*/
@Api(tags = "mybatis-plus测试接口")
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
/**
* description: 新增
* @return: boolean
* @author: weirx
* @time: 2022/1/17 19:11
*/
@ApiOperation(value = "新增")
@GetMapping("/save")
public boolean save() {
UserDO userDO = new UserDO();
userDO.setNickname("大漂亮");
userDO.setSex(SexEnum.MAN);
return userService.save(userDO);
}
}
我们还需要在你的DTO的每个字段上面,添加注解@ApiModelProperty(notes = "用户id"),这个表示你的字段的含义是什么。
到此为止,大功已成,下一节直接看效果。
三、测试
下面就是验证我们的成果,启动网关和用户服务,访问网关接口文档访问地址: http://localhost:8000/doc.html ,注意自己的网关的端口是什么,你的可不一定是8000.
注意:微服务在nacos的注册并不是实时的,换言之,需要去检测服务的心跳,nacos应该是5秒,之后你的接口文档才会出现对应的服务,不是启动好立马出现的。
如果出现404,或者503就说明服务还没完全注册,稍等一会儿,刷新就好了。
至此整合完毕,是不是很简单。
如果有问题,没有搭建成功的,评论区留言,咱们一起找问题。
下一篇文章,主要是关于自动代码生成的,补充前面mybatis-plus-generator没有介绍的遗憾。采用
mybatis-plus-generator + velocity模板引擎,一次定义好代码格式,模板代码从后端到前端一键生成,嘎嘎方便,记得关注啊。