springcloud gateway动态网关整合swagger

写这篇的目的是因为网上很多的博客跟着操作下来根本无法成功,跟着操作要么报404.要么报错Could not render e, see the console,最后没办法,自己研究了一波,最终成功了。

第一步:环境

        这里我是用的是springboot 2.1.12 RELEASE版本,swagger用的是1.9.1.RELEASE,依赖如下,注册中心使用的是nacos,这里不在赘述nacos的搭建了,不明白的可以看我之前的文章centos7搭建nacos集群_取个昵称要人命的博客-CSDN博客

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.1.12.RELEASE</version>
</dependency>


<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-gateway</artifactId>
     <version>2.1.0.RELEASE</version>
</dependency>

<dependency>
     <groupId>com.spring4all</groupId>
     <artifactId>swagger-spring-boot-starter</artifactId>
     <version>1.9.1.RELEASE</version>
</dependency>

第二步:搭建用户服务,实现微服务的自动注册和swagger访问

yml:

spring:
  application:
    name: qsbl-service-user
  cloud:
    nacos:
      discovery:
        server-addr: nacos的ip:nacos的端口

swagger配置类:

/**
 * 如果是springsecurity项目的话
 * 该配置类放在与启动类同一层,不然可能会报错找不到资源路径
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.qsbl.user.service.api.serviceimpl"))//改成自己接口在的包
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("用户服务接口")
                .description("用户服务接口swagger")
                .version("1.0")
                .build();
    }
    /**
     * 防止如果是springsecurity项目无法访问swagger
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 解决静态资源无法访问
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
        // 解决swagger无法访问
        registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        // 解决swagger的js文件无法访问
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

然后启动后访问:成功,用户服务的端口我这里是60001,成功访问get接口

 


第三步:搭建动态网关工程(利用mysql),并访问到user服务

这里不赘述数据库层的操作,相信增删改查的操作是都会的,我用的mysql+mybatis+tkmybatis

数据库表如下:

实体类:QsblRouteDO

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Accessors(chain = true)
@Table(name = "qsbl_route")
public class QsblRouteDO implements Serializable {

    private static final long serialVersionUID = 2383801812375289673L;

    @KeySql(genId = UUIdGenId.class)
    @Id
    private String id;//id

    private String routeId;//路由id

    private String routeName;//路由名称

    private String routePattern;//设备匹配规则

    private String routeType;//路由类型

    private String routeUrl;//路由地址

    private String status;//状态 0正常1失效

    private String isDel;//是否删除 0否1是
}

mapper里添加接口:

@Select("select * from qsbl_route where status = '1' and is_del = '0'")
    List<QsblRouteDO> getAllRoutes();

 Service类:QsblRouteService(最重要)StripPrefix一定要加,不然就是各种报错

@Service
public class QsblRouteService implements ApplicationEventPublisherAware, CommandLineRunner {

    private ApplicationEventPublisher applicationEventPublisher;

    @Resource
    private RouteDefinitionWriter routeDefinitionWriter;

    @Resource
    private QsblRouteMapper qsblRouteMapper;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void loadAllRoutes(){
        List<QsblRouteDO> allRoutes = qsblRouteMapper.getAllRoutes();
        System.out.println(allRoutes);
        for (QsblRouteDO route : allRoutes) {
            loadRoute(route);
        }
    }

    public void loadRoute(QsblRouteDO qsblRouteDO) {
        RouteDefinition definition = new RouteDefinition();
        Map<String, String> predicateParams = new HashMap<>(8);
        PredicateDefinition predicate = new PredicateDefinition();
        URI uri;
        if(RouteTypeEnum.LB_TYPE.getValue().equals(qsblRouteDO.getRouteType())){
            uri = UriComponentsBuilder.fromUriString("lb://"+qsblRouteDO.getRouteUrl()+"/").build().toUri();
        }else {
            uri = UriComponentsBuilder.fromHttpUrl(qsblRouteDO.getRouteUrl()).build().toUri();
        }
        //定义的路由唯一id
        definition.setId(qsblRouteDO.getRouteId());
        //路由转发地址
        predicateParams.put("pattern",qsblRouteDO.getRoutePattern());
        predicate.setName("Path");
        predicate.setArgs(predicateParams);

        // 名称是固定的, 路径去前缀
        FilterDefinition filterDefinition = new FilterDefinition();
        Map<String, String> filterParams = new HashMap<>(8);
        filterDefinition.setName("StripPrefix");
        filterParams.put("_genkey_0", "1");
        filterDefinition.setArgs(filterParams);
        definition.setFilters(Arrays.asList(filterDefinition));

        definition.setPredicates(Arrays.asList(predicate));
        definition.setUri(uri);
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
    }

    /**
     * spring加载完成后自动执行
     * @param args
     * @throws Exception
     */
    @Override
    public void run(String... args) throws Exception {
        loadAllRoutes();
    }
}

yml

server:
  port: 60010

spring:
  application:
    name: qsbl-cloud-gateway
  cloud:
#   gateway:
#      routes:
#        - id: user
#          uri: lb://qsbl-service-lib/
#          filters:
#            - StripPrefix=1
#          predicates:
#            - Path=/user/**
# 加上下面的就会自动注册
#      discovery:
#        locator:
#          enabled: true
    nacos:
      discovery:
        server-addr: nacos的Ip:nacos的端口

启动类:

添加@EnableDiscoveryClient注解

然后启动后访问:成功,网关服务时60010端口,成功访问用户服务的get接口


第四步: 实现网关整合swagger并能访问到微服务的swagger

这一步也就是我开头说的网上很多的博客跟着操作下来根本无法成功,跟着操作要么报404.要么报错Could not render e, see the console的一步。

SwaggerHandler类:照抄就行

@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类:(这里是和网上不一样的地方,重点就是这个get()方法,要能匹配到其他微服务的swagger链接,其实可以发现都一样,都是/v2/api-docs,无非就是在前面匹配一个微服务的id,数据库里查出来就好,和上面动态路由那块一样的道理)

@Component
public class SwaggerProvider implements SwaggerResourcesProvider {

    @Resource
    private QsblRouteMapper qsblRouteMapper;

    @Override
    public List<SwaggerResource> get() {
        List resources = new ArrayList<>();
        List<QsblRouteDO> allRoutes = qsblRouteMapper.getAllRoutes();
        for (QsblRouteDO route : allRoutes) {
            resources.add(swaggerResource(route.getRouteName(), route.getRouteId()));
        }
        return resources;
    }

    private SwaggerResource swaggerResource(String routeName, String routeId) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(routeName);
        swaggerResource.setLocation("/"+routeId+"/v2/api-docs");
        swaggerResource.setSwaggerVersion("2.0");
        return swaggerResource;
    }
}

然后启动后访问:成功

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值