微服务集成Swagger
Swagger现状
在微服务兴起时,对大型项目的调试愈发重要,对众多微服务下每个微服务都有自己的swagger,每次对具体某个项目进行调试时,都需要访问对应的端口号以及项目名的swagger。当微服务不多的情况下是可行的,但是在微服务渐渐多起来时,与前端调试接口时,会成为比较繁琐的一件事。
并且在微服务项目中普遍使用token进行身份验证,在swagger中并没有可以携带请求头的地方,需要解析token时需使用postman工具对接口进行调试,所以接下来记录一下,对swagger进行改造继承,使微服务项目对同一个地址就可以访问所有微服务的swagger并且加载可携带请求头的参数。
技术要点
微服务解决方案使用的SpringCloud ,网关为SpringCloud下的组件Gateway
1.配置好全局跨域配置。跨域配置可以部署在Gateway
2.微服务项目需要有各个项目名称的前缀,因为分发的时候需要项目名称
配置
pom文件
<!--swagger start-->
<!-- 美化swagger 访问swagger-ui.html为原来页面,doc.html为美化页面-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<!--移除swagger-models 1.5.20 依赖,存在Swagger2异常:Illegal DefaultValue null for parameter type integer问题-->
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
<!--swagger end-->
网关跨域配置
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
部署在网关的分发swagger配置
/**
* @description
* @author: Mr.JK
* @create: 2021-07-09 16:05
**/
@Component
@Primary
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v2/api-docs";
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
.forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
.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;
}
}
/**
* @description
* @author: Mr.JK
* @create: 2021-07-09 17:03
**/
@RestController
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("/swagger-resources/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
@GetMapping("/")
public Mono<ResponseEntity> swaggerResourcesN() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
@GetMapping("/csrf")
public Mono<ResponseEntity> swaggerResourcesCsrf() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
然后对具体每个微服务配置一个swagger配置,既可以通过网关进行分发各个微服务的swagger,也可以访问具体的微服务swagger地址。微服务项目带鉴权一般都是在网关进行拦截,对有用户鉴权的项目可根据具体请求头在swagger带不同的参数
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
// 构造token给测试的时候填写
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
tokenPar.name("token").description("用户令牌(不需用户鉴权的不需要传)").modelRef(new ModelRef("string")).parameterType("header")
.required(false).build();
pars.add(tokenPar.build());
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.huoran.data_platform.controller"))
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build().globalOperationParameters(pars);
}
@Bean
UiConfiguration uiConfig() {
return UiConfigurationBuilder.builder().docExpansion(DocExpansion.LIST).operationsSorter(OperationsSorter.ALPHA).build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("xxxx平台微服务接口文档")
.description("xxx")
.contact("developer@mail.com")
.version("1.0")
.build();
}
}
最后访问网关swagger地址就可以进行分发
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!