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

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

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

1 声望
1 粉丝
0 条评论
推荐阅读
公共redis工具类提取
Redis的键值默认使用JDK序列化,为方便排查问题,需自定义Json列化,并加入客户端操作工具类,方便各服务使用。1、定义序列化FastJsonRedisSerializer {代码...} 2、自定义redisTemplate {代码...} 3、操作工具类...

踮脚被吹跑阅读 1.2k

封面图
Postcat 如何生成接口文档,2 分钟学会
Postcat 是一个强大的开源、跨平台(Windows、Mac、Linux、Browsers...)的 API 开发测试工具,支持 REST、Websocket 等协议(即将支持 GraphQL、gRPC、TCP、UDP),帮助你加速完成 API 开发和测试工作。

圆圆大姐头2阅读 370

SpringCloud Gateway 通过 Nacos 配置动态路由 (代码片段)
{代码...}

毛宇鹏阅读 3.9k

装上这个插件,你就能一键生成接口文档!
当有接口对接需求的时候,开发终于不用再担心 API 文档难写了,也不用再一个个接口文档重新写!安装这个 IDEA 插件,可以一步将文档导入到 Postcat。

气势凌人的柿子2阅读 247

除了 Swagger,这个开源 API 工具生成文档更高效!
Swagger 是一个用于生成、描述和调用 RESTful 接口的 Web 服务。通俗的来讲,Swagger 就是将项目中所有(想要暴露的)接口展现在页面上,并且可以进行接口调用和测试的服务。

圆圆大姐头2阅读 207

国产 API 工具天花板,用来搞项目真的不错
随着最近行业的移动化、物联网化、数字化转型、微服务等多种概念的提出,对应的 API 数量已经呈现出爆炸式增长,由此带来的问题就是前后端的接口对接问题越来越来突出,我们能很难找到一个合适的技术工具提高我们...

气势凌人的柿子2阅读 126评论 1

Swagger URL 插件上线,同步生成文档更简单!
本次版本更新主要围绕这几个方面:从 Swagger URL 同步 APIAPI 编辑页面自动调整编辑器高度Windows 安装包自定义 UI但我最想说的还是这个 【从Swagger URL 同步 API】 的部分,也是本次更新的重点。对于中小型(...

Postcat1阅读 373评论 1

1 声望
1 粉丝
宣传栏