Spring Cloud Gateway聚合Swagger文档

踮脚被吹跑

前言

2020年7月份Springfox 3.0.0发布了,增加了对Webflux、OpenApi 3的支持,适应Gateway,微服务中为方便管理各微服务的接口文档,特此来摸索一下Spring Cloud Gateway集成管理,整个过程没什么代码,简单易用

1、环境及工具介绍

1、gradle,一个基于 JVM 的富有突破性构建工具,简化maven的xml繁琐配置
2、nacos,阿里开发的动态服务发现、配置和服务管理平台,appllo不喜欢用就它了
3、knife4j,Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui(swagger2用的好好的为啥要用这玩意???原皮看厌倦了换上新鲜的感觉)
4、Spring Cloud版本用的是Hoxton.RELEASE,SpringBoot版本2.2.1.RELEASE
以上gradle、nacos环境自行百度搭建,比较简单,不做赘述了

2、微服务端

  2.1、先写两个简单的微服务,这里起个很随便的名字(example、cart-service)

端口分别设置为8081和8082

server: 
  port: 8081

build.gradle:

plugins {
    id 'org.springframework.boot' version '2.2.1.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}


group 'com.asan.cart'
version '1.0.0-SNAPSHOT'
sourceCompatibility = 1.8

ext {
    set('springBootVersion', "2.2.1.RELEASE")
    set('springCloudVersion', "Hoxton.RELEASE")
    set('alibabaCloudVersion', "2.2.1.RELEASE")
}
repositories {
    mavenLocal()
    maven {
        url 'http://maven.aliyun.com/nexus/content/groups/public/'
    }
}
configurations {
    compile.exclude module: 'tomcat-embed-el'
    compile.exclude module: 'spring-boot-starter-tomcat'
}
dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile (
            /** 微服务api文档,不需要引入前端ui包 */
            'com.github.xiaoymin:knife4j-micro-spring-boot-starter:3.0.2',
            /**  nacos配置中心 */
            'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config',
            /**  nacos注册与发现中心 */
            'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery',
            'org.springframework.boot:spring-boot-starter-web',
            'org.springframework.boot:spring-boot-starter-undertow' 
    )
}    
dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springBootVersion}"
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
        mavenBom "com.alibaba.cloud:spring-cloud-alibaba-dependencies:${alibabaCloudVersion}"
    }
}    

这里主要就是启动微服务集成knife4j文档,因为tomcat老报错,容器就用的undertow,web默认用的tomcat,如果需要需要使用undertow,增加如下内容排除tomcat依赖:

configurations {
    compile.exclude module: 'tomcat-embed-el'
    compile.exclude module: 'spring-boot-starter-tomcat'
}

两个微服务客户端增加一个配置类:

package com.asan.example.config;

import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;


@Configuration
@EnableSwagger2WebMvc
public class Swagger2Config {

@Bean
public Docket createRestApi(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.asan.example.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        Contact contact = new Contact("阿三", "http://www.asan.com", "asan@163.com");
        return new ApiInfoBuilder()
                .title("example服务文档")
                .description("example服务API文档")
                .contact(contact)
                .version("1.0")
                .build();
    }
}

增加启动类ExampleApplication

package com.asan.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class ExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ExampleApplication.class, args);
    }

}

接着来一个Controller做文档展示:

Controller这里自己随便写吧,能看到就行,这里也贴一下吧显得不那么空

package com.asan.example.controller;

import com.alibaba.fastjson.JSON;
import com.asan.example.entity.Cat;
import com.asan.example.pojo.dto.CatDto;
import com.asan.example.pojo.vo.PageResult;
import com.asan.example.pojo.vo.ResultVO;
import com.asan.example.service.impl.CatServiceImpl;
import com.asan.example.util.BeanCopierUtil;
import com.asan.example.util.RedisClient;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import tk.mybatis.mapper.entity.Example;

@RestController
@RequestMapping("/v1/example/cat")
@Slf4j
public class CatController {

    @Autowired
    private CatServiceImpl catService;
    @Autowired
    private RedisClient redisClient;

    @ApiOperation("获取信息")
    @GetMapping
    public ResultVO<String> getCat(@ApiParam("主键") @RequestHeader("cid") Integer cid) {
        Cat cat = catService.selectById(cid);
        if (cat != null) {
            // 测试缓存
            redisClient.setForTimeMIN("SOA:TEXMPLE:CAT:"+cid.toString(), JSON.toJSONString(cat), 30);
        }
        return new ResultVO(cat);
    }

    @ApiOperation("新增信息")
    @PostMapping
    public ResultVO<String> addCat(@RequestBody CatDto catDto) {
        Cat cat = new Cat();
        BeanCopierUtil.copy(catDto, cat);
        catService.insert(cat);
        return new ResultVO(Constants.RESULT_SUCCESS);
    }

    @ApiOperation("修改信息")
    @PutMapping
    public ResultVO<String> updateCat(@RequestBody CatDto catDto) {
        Cat cat = new Cat();
        BeanCopierUtil.copy(catDto, cat);
        catService.updateById(cat);
        return new ResultVO(Constants.RESULT_SUCCESS);
    }

    @ApiOperation("删除信息")
    @DeleteMapping
    public ResultVO<String> deleteCat(@ApiParam("主键") @RequestHeader("cid") Integer cid) {
        catService.deleteById(cid);
        return new ResultVO(Constants.RESULT_SUCCESS);
    }
}

然后复制项目更改名称为cart-service,settings-gradle记得也改下
controller里仅修改了路径和类名CartController

3、gateway集成微服务端

启动类一样复制一个过来,改名GatewayApplication
build.gradle文件增加依赖

compile (
            /** api文档,包含前端ui包 */
            'com.github.xiaoymin:knife4j-spring-boot-starter:3.0.2',
            /**  nacos配置中心 */
            'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config',
            /**  nacos注册与发现中心 */
            'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery',
            'javax.servlet:javax.servlet-api:3.1.0',
            'org.springframework.cloud:spring-cloud-starter-gateway'
    )

下面增加一个配置类

package com.asan.gateway.config;

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.Configuration;
import org.springframework.context.annotation.Primary;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebFlux;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Jaakko
 */
@Configuration
@Primary
@EnableSwagger2WebFlux
public class SwaggerResourceConfig implements SwaggerResourcesProvider {
    public static final String API_URI = "v2/api-docs";

    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;

    public SwaggerResourceConfig(RouteLocator routeLocator, GatewayProperties gatewayProperties) {
        this.routeLocator = routeLocator;
        this.gatewayProperties = gatewayProperties;
    }

    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        //获取所有路由的ID
        routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
        //过滤出配置文件中定义的路由->过滤出Path Route Predicate->根据路径拼接成api-docs路径->生成SwaggerResource
        gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route ->
            route.getPredicates().stream()
                    .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
                    .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;
    }
}

gateway nacos配置 

server: 
  port: 9200 
spring: 
  cloud: 
  gateway:
    #配置路由路径 
    routes:
    - id: example 
      uri: lb://example 
      predicates: 
      - Path=/example/** 
      filters: 
      - StripPrefix=1 
    - id: cart-service 
      uri: lb://cart-service 
      predicates: 
      - Path=/cart-service/** 
      filters: 
      - StripPrefix=1 
    discovery: 
      locator: 
        #开启从注册中心动态创建路由的功能
        enabled: true
        #使用小写服务名,默认是大写
        lower-case-service-id: true

启动两个微服务端,再启动gateway服务

浏览器访问:localhost:9200/doc.html

这里某些依赖手动加进去的,并未实际验证!!!(你可能觉得坑,但八九不离十),可能存在兼容问题,大致应该可以,有问题可以留言讨论!!!

效果图这里就不截了,自行玩耍

阅读 2.7k
1 声望
1 粉丝
0 条评论
1 声望
1 粉丝
文章目录
宣传栏