SpringCloud学习(八)----- Gateway网关及其他微服务接入Swagger接口文档(Eureka注册中心)
Swagger是一个规范且完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。Swagger 的目标是对 REST API 定义一个标准且和语言无关的接口,可以让人和计算机拥有无须访问源码、文档或网络流量监测就可以发现和理解服务的能力。当通过 Swagger 进行正确定义,用户可以理解远程服务并使用最少实现逻辑与远程服务进行交互。与为底层编程所实现的接口类似,Swagg
SpringCloud版本:2021.0.1 SpringBoot版本:2.6.3 Swagger版本:2.9.2
系列文章
SpringCloud学习(一)----- Eureka搭建SpringCloud学习(一)----- Eureka搭建SpringCloud学习(一)----- Eureka搭建
SpringCloud学习(二)----- SpringBoot Admin搭建(与Eureka整合)
SpringCloud学习(三)----- Gatewayw网关搭建
SpringCloud学习(四)----- Gatewayw网关完善(限流)
SpringCloud学习(五)----- Gatewayw网关完善(Resilience4j断路器)
SpringCloud学习(六)----- Gatewayw网关完善(防止SQL注入)
SpringCloud学习(七)----- 使用Feign调用别的微服务的方法
SpringCloud学习(八)----- Gateway网关及其他微服务接入Swagger接口文档
参考文章:
Spring Cloud Gateway整合Swagger聚合微服务系统API文档(非Zuul)
swagger-ui升级版swagger-bootstrap-ui
今天,我们要学习的是对接Swagger,为的是解决我们写接口的时候,能更好以及更快更方便的对接口进行测试,也能更简单的在编写代码的时候就顺便把接口文档也写了,而不用在写完接口后还得重新去看接口来写接口文档。
一、什么是Swagger?
Swagger 是一个规范且完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
Swagger 的目标是对 REST API 定义一个标准且和语言无关的接口,可以让人和计算机拥有无须访问源码、文档或网络流量监测就可以发现和理解服务的能力。当通过 Swagger 进行正确定义,用户可以理解远程服务并使用最少实现逻辑与远程服务进行交互。与为底层编程所实现的接口类似,Swagger 消除了调用服务时可能会有的猜测。
二、Swagger 的优势
-
支持 API 自动生成同步的在线文档:使用 Swagger 后可以直接通过代码生成文档,不再需要自己手动编写接口文档了,对程序员来说非常方便,可以节约写文档的时间去学习新技术。
-
提供 Web 页面在线测试 API:光有文档还不够,Swagger 生成的文档还支持在线测试。参数和格式都定好了,直接在界面上输入参数对应的值即可在线测试接口。
三、Swagger如何集成到SpringCloud框架
(1)、Gateway网关配置
首先,先加上pom依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
再在项目里新建个swagger文件夹,用于存放swagger配置的相关文件。
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));
}
@GetMapping("")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
SwaggerProvider文件:
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.List;
@Component
@Primary
public class SwaggerProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v2/api-docs";
public static final String EUREKA_SUB_PRIX = "ReactiveCompositeDiscoveryClient_";
private final DiscoveryClientRouteDefinitionLocator routeLocator;
public SwaggerProvider(DiscoveryClientRouteDefinitionLocator routeLocator) {
this.routeLocator = routeLocator;
}
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
//从DiscoveryClientRouteDefinitionLocator 中取出routes,构造成swaggerResource
routeLocator.getRouteDefinitions().subscribe(routeDefinition -> {
resources.add(swaggerResource(routeDefinition.getId().substring(EUREKA_SUB_PRIX.length()),routeDefinition.getPredicates().get(0).getArgs().get("pattern").replace("/**", API_URI)));
});
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
这样在Gateway网关所需要的配置也就已经完成了,后面就是对服务的配置了。
(2)、服务的配置
首先,依旧是先加上pom依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
再在服务配置文件application.yml上加上控制swagger是否开启的配置已经spring的路径匹配规则,不然会出现问题,因为我用的Swagger版本是2.9.2,但SpringBoot的版本比较新,所以路径匹配的规则会不一样
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
swagger:
enabled: true
之后,再新建个Swagger的接口配置文件SwaggerConfig。
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.*;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @description: 配置描述swagger的描述
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
//是否开启swagger,正式环境一般是需要关闭的,可根据springboot的多环境配置进行设置
@Value(value = "${swagger.enabled}")
Boolean swaggerEnabled;
@Bean
public Docket createRestApi() {
//给header添加参数token
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<>();
tokenPar.name("JSESSIONID")
.description("令牌")
.modelRef(new ModelRef("string"))
.parameterType("header").required(false).build();
pars.add(tokenPar.build());
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
.title("接口列表")
.description("数据相关")
.contact(new Contact("chenai", "http://www.chenai.cn","cjwzyd@163.com"))
.version("1.0.0").build())
// 是否开启
.enable(swaggerEnabled).select()
// 扫描的路径包 -- 路径记得改成自己的
.apis(RequestHandlerSelectors.basePackage("com.*.controller"))
// 指定路径处理 PathSelectors.any()
// 代表所有的路径
.paths(PathSelectors.any())
.build().globalOperationParameters(pars)
.ignoredParameterTypes(HttpSession.class, HttpServletRequest.class, HttpServletResponse.class);
}
@Bean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList();
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
String basePath = webEndpointProperties.getBasePath();
EndpointMapping endpointMapping = new EndpointMapping(basePath);
boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
}
private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}
}
好了,服务这边的配置就完成了,接下来就启动网关、服务以及Eureka就可以啦。
访问的地址是{gateway-ip}:{gateway-port}/swagger-ui.html
优化:
虽然已经把Swagger集成到SpringCLoud里的,但也许有的人会觉得当前这个Swagger的界面不好看,对接口的测试也不是很方便,那么,其实我们是可以对当前的Swagger的UI界面进行升级的,也就是用Boostrap的框架来进行优化,不过也不难,导几个包,和加个注解就可以解决的。(一开始是用knife4j的,也就是swagger-bootstrap-ui的进阶版,但个人觉得不是很习惯knife4j的操作,所以就用回swagger-bootstrap-ui,具体用啥看个人喜好)
首先,导入pom依赖,这个是在Gateway网关和对应的服务项目里都得引入的。
<!--swaggerui 几个自定义界面方便查看接口-->
<!--访问路径:http://localhost:8080/doc.html-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
<!-- # 增加两个配置解决 NumberFormatException -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.22</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.22</version>
</dependency>
配置的话在Gateway网关里就不用修改,但在微服务里的SwaggerConfig文件就得加上@EnableSwaggerBootstrapUI这个注解,然后就可以重启项目了,没错,就是这么简单,引包,加注解就可以了。
然后,这个访问地址就和之前的不一样了,当然,之前的地址也还是可以用的,新的访问地址为:{gateway-ip}:{gateway-port}/doc.html。
界面的大部分功能都一样,但会比原生的swagger界面好看并排版更加明显,容易理解。
进入地址后看到这个页面就说明成功啦。
至于knife4j的配置的话,只要把依赖换成knife4j的依赖就可以了,其他的都不用改,也不用加@EnableSwaggerBootstrapUI这个注解,不过一样是Gateway和微服务都得加上依赖,然后重启就可以了。
至于knife4j里其实还有其他的功能,不过就不在这里叙述了,有兴趣的同学可以去我上面参考的那篇knife4j的文章里看,里面比较详细。
更多推荐
所有评论(0)