天天看点

【Spring Cloud Alibaba】Swagger 聚合接口文档【Spring Cloud Alibaba】Swagger 聚合接口文档微信公众号

文章目录

  • 【Spring Cloud Alibaba】Swagger 聚合接口文档
    • 1、Swagger
    • 2、单个服务集成 Swagger
    • 3、Gateway 网关聚合 Swagger
  • 微信公众号

【Spring Cloud Alibaba】Swagger 聚合接口文档

1、Swagger

Swagger 没有什么好介绍的了,在 Spring Boot 中是最常用的生成接口文档的工具,这里主要说在为服务中怎么使用 Swagger 生成聚合文档

2、单个服务集成 Swagger

在需要的服务中增加依赖,swagger-spring-boot-starter,后续可以改主题,这里先用默认的

${swagger-spring-boot-starter.version}

版本为

<swagger-spring-boot-starter.version>1.9.0.RELEASE</swagger-spring-boot-starter.version>
           
<dependency>
            <groupId>com.spring4all</groupId>
            <artifactId>swagger-spring-boot-starter</artifactId>
            <version>${swagger-spring-boot-starter.version}</version>
        </dependency>
           

然后增加一个文档配置

package cn.tellsea.config;

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.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * 接口文档
 *
 * @author Tellsea
 * @date 2022/01/04
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig {

    /**
     * 创建API
     */
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                // 详细定制
                .apiInfo(apiInfo("1.0.0"))
                .select()
                // 指定当前包路径
                .apis(RequestHandlerSelectors.basePackage("cn.tellsea.controller"))
                // 扫描所有
                .paths(PathSelectors.any())
                .build();
    }

    /**
     * 添加摘要信息
     *
     * @param version
     * @return
     */
    private ApiInfo apiInfo(String version) {
        // 用ApiInfoBuilder进行定制
        return new ApiInfoBuilder()
                .title("Seata订单服务")
                .version(version)
                .build();
    }
}

           

启动服务,访问链接,我这里使用的是上一次的三个服务,分别都增加了依赖和配置

http://localhost:8007/swagger-ui.html
           
【Spring Cloud Alibaba】Swagger 聚合接口文档【Spring Cloud Alibaba】Swagger 聚合接口文档微信公众号
http://localhost:8008/swagger-ui.html
           
【Spring Cloud Alibaba】Swagger 聚合接口文档【Spring Cloud Alibaba】Swagger 聚合接口文档微信公众号
http://localhost:8009/swagger-ui.html
           
【Spring Cloud Alibaba】Swagger 聚合接口文档【Spring Cloud Alibaba】Swagger 聚合接口文档微信公众号

看后台日志,发现一个报错

java.lang.NumberFormatException: For input string: ""
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[na:1.8.0_181]
	at java.lang.Long.parseLong(Long.java:601) ~[na:1.8.0_181]
	at java.lang.Long.valueOf(Long.java:803) ~[na:1.8.0_181]
	at io.swagger.models.parameters.AbstractSerializableParameter.getExample(AbstractSerializableParameter.java:412) ~[swagger-models-1.5.20.jar:1.5.20]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:689) [jackson-databind-2.11.3.jar:2.11.3]
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755) [jackson-databind-2.11.3.jar:2.11.3]
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) [jackson-databind-2.11.3.jar:2.11.3]
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119) [jackson-databind-2.11.3.jar:2.11.3]
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79) [jackson-databind-2.11.3.jar:2.11.3]
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18) [jackson-databind-2.11.3.jar:2.11.3]
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) [jackson-databind-2.11.3.jar:2.11.3]
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755) [jackson-databind-2.11.3.jar:2.11.3]
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) [jackson-databind-2.11.3.jar:2.11.3]
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) [jackson-databind-2.11.3.jar:2.11.3]
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755) [jackson-databind-2.11.3.jar:2.11.3]

           

是因为 Swagger2 中 @ApiModelProperties 如果为数字类型,但添加注解后,又不指定 example 的值,会默认为"",swagger 在后续处理的时候强行转化空字符串就会抛出异常。这也是 Swagger2 的 bug

第 1 种:增加配置

logging.level.io.swagger.models.parameters.AbstractSerializableParameter=ERROR

或者

# 解决Swagger报错:For input string: ""
logging:
  level:
    io:
      swagger:
        models:
          parameters:
            AbstractSerializableParameter: ERROR
           

第二种:降低 Swagger 版本,不推荐

3、Gateway 网关聚合 Swagger

创建一个 spring-cloud-alibaba-seata-gateway 网关服务,依赖如下

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

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

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

        <dependency>
            <groupId>com.spring4all</groupId>
            <artifactId>swagger-spring-boot-starter</artifactId>
            <version>${swagger-spring-boot-starter.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
           

修改 application.properties 配置文件

server.port=8010
spring.application.name=spring-cloud-alibaba-seata-gateway
management.server.port=9010
management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

# Nacos注册信息
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
spring.cloud.nacos.discovery.namespace=sandbox-configuration
           

启动类增加

创建聚合 Swagger 文档的配置类

package cn.tellsea.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 聚合Swagger文档
 *
 * @author Tellsea
 * @date 2022/01/04
 */
@Component
public class MySwaggerResourceProvider implements SwaggerResourcesProvider {

    /**
     * swagger2默认的url后缀
     */
    private static final String SWAGGER2URL = "/v2/api-docs";

    /**
     * 网关路由
     */
    private final RouteLocator routeLocator;

    /**
     * 网关应用名称
     */
    @Value("${spring.application.name}")
    private String self;

    @Autowired
    public MySwaggerResourceProvider(RouteLocator routeLocator) {
        this.routeLocator = routeLocator;
    }

    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routeHosts = new ArrayList<>();
        // 由于我的网关采用的是负载均衡的方式,因此我需要拿到所有应用的serviceId
        // 获取所有可用的host:serviceId
        routeLocator.getRoutes().filter(route -> route.getUri().getHost() != null)
                .filter(route -> !self.equals(route.getUri().getHost()))
                .subscribe(route -> routeHosts.add(route.getUri().getHost()));

        // 记录已经添加过的server,存在同一个应用注册了多个服务在nacos上
        Set<String> dealed = new HashSet<>();
        routeHosts.forEach(instance -> {
            // 拼接url,样式为/serviceId/v2/api-info,当网关调用这个接口时,会自动通过负载均衡寻找对应的主机
            String url = "/" + instance + SWAGGER2URL;
            if (!dealed.contains(url)) {
                dealed.add(url);
                SwaggerResource swaggerResource = new SwaggerResource();
                swaggerResource.setUrl(url);
                swaggerResource.setName(instance);
                resources.add(swaggerResource);
            }
        });
        return resources;
    }
}

           

重写 Swagger 访问地址

package cn.tellsea.controller;

import cn.tellsea.config.MySwaggerResourceProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.swagger.web.*;

import java.util.List;

/**
 * 重写Swagger访问地址
 *
 * @author Tellsea
 * @date 2022/01/04
 */
@RestController
@RequestMapping("/swagger-resources")
public class SwaggerResourceController {
    private MySwaggerResourceProvider swaggerResourceProvider;

    @Autowired
    public SwaggerResourceController(MySwaggerResourceProvider swaggerResourceProvider) {
        this.swaggerResourceProvider = swaggerResourceProvider;
    }

    @RequestMapping(value = "/configuration/security")
    public ResponseEntity<SecurityConfiguration> securityConfiguration() {
        return new ResponseEntity<>(SecurityConfigurationBuilder.builder().build(), HttpStatus.OK);
    }

    @RequestMapping(value = "/configuration/ui")
    public ResponseEntity<UiConfiguration> uiConfiguration() {
        return new ResponseEntity<>(UiConfigurationBuilder.builder().build(), HttpStatus.OK);
    }

    @RequestMapping
    public ResponseEntity<List<SwaggerResource>> swaggerResources() {
        return new ResponseEntity<>(swaggerResourceProvider.get(), HttpStatus.OK);
    }
}

           

启动服务访问网关地址

http://localhost:8010/swagger-ui.html
           

右上角已经读取出了各个服务的应用名称,直接切换即可查看文档,下面是账号服务的接口文档

【Spring Cloud Alibaba】Swagger 聚合接口文档【Spring Cloud Alibaba】Swagger 聚合接口文档微信公众号

订单服务文档

【Spring Cloud Alibaba】Swagger 聚合接口文档【Spring Cloud Alibaba】Swagger 聚合接口文档微信公众号

库存服务文档

【Spring Cloud Alibaba】Swagger 聚合接口文档【Spring Cloud Alibaba】Swagger 聚合接口文档微信公众号

到此,Swagger 聚合接口文档完成

微信公众号

【Spring Cloud Alibaba】Swagger 聚合接口文档【Spring Cloud Alibaba】Swagger 聚合接口文档微信公众号