【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 聚合接口文档
http://localhost:8008/swagger-ui.html
【Spring Cloud Alibaba】Swagger 聚合接口文档
http://localhost:8009/swagger-ui.html

【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

启动类增加

@EnableDiscoveryClient

创建聚合 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 聚合接口文档到此,Swagger 聚合接口文档完成

微信公众号


原文始发于微信公众号(花海里):【Spring Cloud Alibaba】Swagger 聚合接口文档

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69408.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!