首页 > 其他分享 >springcloud:gateway聚合swagger 下篇(十二)

springcloud:gateway聚合swagger 下篇(十二)

时间:2023-02-03 10:05:43浏览次数:64  
标签:网关 return springcloud springframework import org swagger gateway


0. 引言

上一章我们之前讲解了在​​单个服务中部署swagger​​,但每次都需要在不同的端口中访问不同服务的swagger-ui。那么本期我们就来讲解一下,如何从一个统一的入口访问不同服务的swagger

1. 思路

我们之前讲过网关的概念,如果不清楚的可以查看之前的博文。那么想象一下,我们是不是可以从一个统一的入口访问接口文档,然后通过路由转发将实际请求转发到对应的微服务上

如果看过之前讲​​网关gateway​​这篇内容的同学,听到这里是不是有点思路了?这不就是网关路由转发吗?统一的入口就是网关的入口。再根据服务名转发到不同的微服务中的swagger-ui。

那么我们就可以把网关作为统一入口,同时也在网关服务中配置上swagger,将网关作为swagger-server。各个微服务中也部署各自的单机版的swagger,作为swagger-client。之后会通过路由转发将对swagger的请求转发到各个微服务中。

springcloud:gateway聚合swagger 下篇(十二)_spring

1.1 什么是 v2/api-docs?

在开始具体实现之前,先要给大家说明几个概念,帮助大家理解后续的代码。首先我们的swagger文档信息实际上是通过​​v2/api-docs​​这个接口获取的,这个接口是swagger自带的。

我们可以调用一个微服务的​​v2/api-docs​​接口试试:

会发现他返回的json数据,就是我们要在页面中展示的接口文档数据。所以我们通过网关来实现swagger的接口转发,实际上转发的就是​​v2/api-docs​​接口

springcloud:gateway聚合swagger 下篇(十二)_spring_02

1.2 什么是swagger-resources?

这也是一个接口地址,默认这个接口获取的是本服务的api-docs访问路径,我们可以通过重写这个接口实现获取到所有微服务的api-docs访问路径。

本机服务的swagger-resources接口调用

springcloud:gateway聚合swagger 下篇(十二)_ide_03


网关中重写后的swagger-resources接口调用

springcloud:gateway聚合swagger 下篇(十二)_ide_04


具体针对这个接口的实现,我们在下面的的实操中讲解

2. 完整实现

1、在各微服务中部署单机版swagger,不清楚怎么部署的请看上一篇:
​接口文档自动生成器swagger详解 上篇​​

​之后的操作均在网关服务中进行!!!​

2、网关服务中引入依赖

目前swagger官方已经更新到了swagger3了,但是大多数开发中仍然在使用swagger2,所以我们这里使用swagger2

<!--  swagger2      -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

3、创建swagger配置文件SwaggerConfig,该类主要用于提供两个bean: securityConfiguration和uiConfiguration。这两个bean在后续会被调用

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;

/**
* swagger配置类
* @author whx
* @date 2022/4/22
*/
@Configuration
public class SwaggerConfig {

@Bean
public SecurityConfiguration securityConfiguration(){
return SecurityConfigurationBuilder.builder().build();
}

@Bean
public UiConfiguration uiConfiguration(){
return UiConfigurationBuilder.builder().build();
}

}

4、再创建swagger的数据资源类,这个类主要用于提供swagger各种资源。

在访问swagger-ui.html页面的时候,主要就是通过访问以下接口来获取文档数据

import lombok.AllArgsConstructor; 
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.List;
import java.util.Optional;

/**
* swagger的数据接口
* 在访问swagger-ui中会拉去此接口的数据
* @author whx
* @date 2022/4/22
*/
@RestController
@RequestMapping("/swagger-resources")
@AllArgsConstructor
public class SwaggerHandler {

private final SecurityConfiguration securityConfiguration;
private final UiConfiguration uiConfiguration;
private final SwaggerResourcesProvider swaggerResourcesProvider;

@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<List<SwaggerResource>>> swaggerResources(){
return Mono.just((new ResponseEntity<>(swaggerResourcesProvider.get(),HttpStatus.OK)));
}
}

5、创建swagger资源配置类,该类主要用于聚合其他微服务中Swagger的api-docs访问路径

import lombok.AllArgsConstructor;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
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.*;

/**
* 聚合swagger配置类
* @author whx
* @date 2022/4/22
*/
@Primary
@Component
@AllArgsConstructor
public class Swagger2ResourceProvider implements SwaggerResourcesProvider {

/**
* swagger默认的url后缀
*/
private static final String API_URI = "v2/api-docs";
/**
* 网关配置项,对应配置文件中配置的spring.cloud.gateway所有子项
*/
private final GatewayProperties gatewayProperties;
/**
* 网关路由
*/
private final RouteLocator routeLocator;

@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(route -> route.getPredicates().stream()
// 忽略配置文件中断言中配置的Path为空的配置项
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
// 将Path中的路由地址由**改为v2/api-docs,swagger就是通过这个地址来获取接口文档数据的,可以通过访问:ip:port/v2/api-docs来体会接口数据
.forEach(predicateDefinition -> resources
.add(swaggerResource(route.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;
}
}

6、修改网关配置文件,将需要聚合swagger的微服务路由配置上

spring:
cloud:
routes:
# id 显示到页面上的名称
- id: 商品服务 product-server
# lb://xxx, xxx为服务名
uri: lb://product-server
predicates:
# Path=/xxx/**,xxx为服务名
- Path=/product-server/**
- id: 订单服务 order-server
uri: lb://order-server
predicates:
- Path=/order-server/**

7、如果网关没有配置鉴权的话,到这里就配置完成了,但是因为我们的网关里一般都配置了鉴权,所以我们还需要swagger的相关路径忽略鉴权。这里根据之前博客中的网关模块来演示

添加无需鉴权的路径​​ "/**/v2/api-docs","/**/swagger-ui.html","/**/swagger-resources/**"​

private final String[] skipAuthUrls = new String[]{"/login/check","/user/register",
"/**/v2/api-docs","/**/swagger-ui.html","/**/swagger-resources/**"};

并且将之前的过滤方法调整为正则匹配

public boolean isSkipUrl(String url) {
if(StringUtils.isEmpty(url)){
return false;
}
AntPathMatcher matcher = new AntPathMatcher();
for (String skipAuthUrl : skipAuthUrls) {
if(matcher.match(skipAuthUrl, url)){
return true;
}
}
return false;
}

完整代码

import com.example.gatewaytoken.util.JWTUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
* @author whx
* @date 2022/4/12
*/
@Component
public class TokenFilter implements GlobalFilter, Ordered{

private final String[] skipAuthUrls = new String[]{"/login/check","/user/register",
"/**/v2/api-docs","/**/swagger-ui.html","/**/swagger-resources/**"};

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String url = exchange.getRequest().getURI().getPath();
// 跳过不需要验证的路径
if (isSkipUrl(url)) {
return chain.filter(exchange);
}
ServerHttpResponse response = exchange.getResponse();
// 从请求头中取得token
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
// token是否为空
if (StringUtils.isEmpty(token)) {
return fail(response,"token为空,鉴权失败");
}
// 请求中的token是否有效
String userId = JWTUtil.getVal(token,"userId").toString();
if(StringUtils.isEmpty(userId)){
return fail(response,"token不合法");
}
// 校验token是否过期
if(JWTUtil.isExpiration(token)){
return fail(response,"token已过期");
}
//如果各种判断都通过,执行chain上的其他业务逻辑
return chain.filter(exchange);
}

private Mono<Void> fail(ServerHttpResponse response,String message){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
DataBuffer buffer = response.bufferFactory().wrap(message.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Flux.just(buffer));
}

/**
* 判断当前访问的url是否开头URI是在配置的忽略url列表中
*
* @param url
* @return
*/
public boolean isSkipUrl(String url) {
if(StringUtils.isEmpty(url)){
return false;
}
AntPathMatcher matcher = new AntPathMatcher();
for (String skipAuthUrl : skipAuthUrls) {
if(matcher.match(skipAuthUrl, url)){
return true;
}
}
return false;
}

@Override
public int getOrder() {
return 0;
}
}

8、启动gateway及其他添加了swagger的微服务

9、访问:http://localhost/swagger-ui.html

如图所示,我们可以在右上角切换文档服务。至此我们的gateway聚合swagger就配置完成了。

当然我们还可以把swagger的配置封装成一个工具服务,只需要引入这个服务,就不用再单独配置了,这一点大家可以先尝试看看,我们会在后续的讲解中演示

springcloud:gateway聚合swagger 下篇(十二)_ide_05

演示代码地址

​gateway聚合swagger代码​

关注公众号 Elasticsearch之家,了解更多新鲜内容


标签:网关,return,springcloud,springframework,import,org,swagger,gateway
From: https://blog.51cto.com/u_15952602/6034923

相关文章

  • springcloud:接口文档自动生成器swagger详解 上篇(十一)
    0.引言在微服务的开发工作中,前后端的协同开发是必不可少的,随着服务数与接口数的增加,手写接口文档成为了一个苦活累活,很多程序员对此也很抵触。同时我们也需要有一个地方来......
  • springcloud:安装rabbitmq并配置延迟队列插件
    0.引言本期主要讲解如何利用docker快速安装rabbitmq并且配置延迟队列插件1.docker安装1.1安装rabbitmq1、下载镜像dockerpullrabbitmq2、安装镜像dockerrun-d--host......
  • Swagger跨域
    什么是跨域跨域是指从一个域名的网页去请求另一个域名的资源。比如从www.baidu.com页面去请求www.google.com的资源。跨域的严格一点的定义是:只要协议,域名,端口有任何一......
  • Gateway网关(快速入门、断言工厂、过滤器工厂、全局过滤器),解决跨域问题
    (目录)Gateway服务网关SpringCloudGateway是SpringCloud的一个全新项目,该项目是基于Spring5.0,SpringBoot2.0和ProjectReactor等响应式编程和事件流技术开......
  • Prometheus Pushgateway配置笔记
    前言pushgateway的安装不再赘述,通用的操作最后以daemonlized方式运行。在Prometheus中给pushgateway上报的数据单独定义一个job:需要注意的点:pushgateway本身没有任何......
  • gateway隐藏服务名
    配置spring.cloud.gateway.routes[0].id=baldr-constructor-serverspring.cloud.gateway.routes[0].uri=lb://baldr-constructor-serverspring.cloud.gateway.routes[0......
  • Swagger与Knife4j知识概括
    Swagger与Knife4j知识概括   Swagger与Knife4j知识概括   Swagger使用   Swagger常用注解   Swagger拓展皮肤   Knife4j简介   OpenAPI简介   ......
  • springcloud alibaba 整合nacos&dubbo
    1.创建父模块创建maven模块,不用勾选任何东西依赖<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="......
  • python处理apiDoc转swagger
    python处理apiDoc转swagger需要转换的接口现在我需要转换的接口全是nodejs写的数据,而且均为post传输的json格式接口apiDoc格式apiDoc代码中的格式如下:/***@api{......
  • Springcloud之Gateway入门
    API网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信......