gateway整合swagger3.0+knife4j增強(qiáng)(完整版)

之前寫的整合文章還有些缺陷,本此全部處理。
參考資料:

官方文檔地址:knife4j (xiaominfo.com) (谷歌打不開就用ie)
github項(xiàng)目:microservices-platform-master
地址:https://github.com/Aisii/microservices-platform-master
博客:https://blog.csdn.net/qq_39878940/article/details/123181951
博客:http://www.itdecent.cn/p/aef7d953ae70

思路:

  1. 將swagger模塊抽出為一個(gè)公共模塊,或者是自定義springboot starter模塊(本文采用后者),供其他模塊快速整合swagger。
  2. gateway服務(wù)配置所有服務(wù)swagger文檔聚合

實(shí)現(xiàn):

step1:自定義 swagger-springboot-starter
1.1 引入依賴

<dependencies>
        <!-- swagger相關(guān)依賴 start -->

        <!-- swagger核心依賴 -->
        <!-- swagger ui美化依賴 -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
        <!-- swagger相關(guān)依賴 end -->

        <!-- springboot 依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!-- 讀取配置文件依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

1.2 swagger 屬性配置實(shí)體類

/**
 * swagger2 屬性配置
 *
 * @author zlt Not registered via @EnableConfigurationProperties, marked as Spring component, or scanned via @ConfigurationPropertiesScan
 * @date 2018/11/18 9:17
 */
@Data
@ConfigurationProperties("swagger")
public class SwaggerProperties {
    /**
     * 是否開啟swagger
     **/
    private Boolean enabled;
    /**
     * 標(biāo)題
     **/
    private String title = "";
    /**
     * 描述
     **/
    private String description = "";
    /**
     * 版本
     **/
    private String version = "";
    /**
     * 許可證
     **/
    private String license = "";
    /**
     * 許可證URL
     **/
    private String licenseUrl = "";
    /**
     * 服務(wù)條款URL
     **/
    private String termsOfServiceUrl = "";
    /**
     * 聯(lián)系人
     **/
    private Contact contact = new Contact();

    /**
     * swagger會解析的包路徑
     **/
    private String basePackage = "";

    /**
     * swagger會解析的url規(guī)則
     **/
    private List<String> basePath = new ArrayList<>();
    /**
     * 在basePath基礎(chǔ)上需要排除的url規(guī)則
     **/
    private List<String> excludePath = new ArrayList<>();

    /**
     * 分組文檔
     **/
    private Map<String, DocketInfo> docket = new LinkedHashMap<>();

    /**
     * host信息
     **/
    private String host = "";

    /**
     * 全局參數(shù)配置
     **/
    private List<GlobalOperationParameter> globalOperationParameters;

    @Setter
    @Getter
    public static class GlobalOperationParameter {
        /**
         * 參數(shù)名
         **/
        private String name;

        /**
         * 描述信息
         **/
        private String description;

        /**
         * 指定參數(shù)類型 ScalarType.STRING
         **/
        private String modelRef;

        /**
         * 參數(shù)放在哪個(gè)地方
         * QUERY("query"),
         * HEADER("header"),
         * PATH("path"),
         * COOKIE("cookie"),
         * FORM("form"),
         * FORMDATA("formData"),
         * BODY("body");
         **/
        private String parameterType;

        /**
         * 參數(shù)是否必須傳
         **/
        private String required;
    }

    @Data
    public static class DocketInfo {
        /**
         * 標(biāo)題
         **/
        private String title = "";
        /**
         * 描述
         **/
        private String description = "";
        /**
         * 版本
         **/
        private String version = "";
        /**
         * 許可證
         **/
        private String license = "";
        /**
         * 許可證URL
         **/
        private String licenseUrl = "";
        /**
         * 服務(wù)條款URL
         **/
        private String termsOfServiceUrl = "";

        private Contact contact = new Contact();

        /**
         * swagger會解析的包路徑
         **/
        private String basePackage = "";

        /**
         * swagger會解析的url規(guī)則
         **/
        private List<String> basePath = new ArrayList<>();
        /**
         * 在basePath基礎(chǔ)上需要排除的url規(guī)則
         **/
        private List<String> excludePath = new ArrayList<>();

        private List<GlobalOperationParameter> globalOperationParameters;
    }

    @Data
    public static class Contact {
        /**
         * 聯(lián)系人
         */
        private String name = "";
        /**
         * 聯(lián)系人url
         */
        private String url = "";
        /**
         * 聯(lián)系人email
         */
        private String email = "";
    }
}

1.3 構(gòu)建文檔配置

package com.ly.tulip.commons.swagger.config;

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.RequestParameterBuilder;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.RequestParameter;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 類說明: swagger 配置類
 *
 * @author wqf
 * @date 2022/7/26 10:23
 */
@Configuration
@EnableOpenApi     //開啟 Swagger3,可以不寫
@EnableKnife4j     //開啟 knife4j,可以不寫
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerAutoConfiguration implements BeanFactoryAware {

    private static final String AUTH_KEY = "Authorization";

    private BeanFactory beanFactory;

    @Bean
    @ConditionalOnMissingBean
    public SwaggerProperties swaggerProperties() {
        return new SwaggerProperties();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "tulip.swagger.enabled", matchIfMissing = true)
    public List<Docket> createRestApi(SwaggerProperties swaggerProperties) {
        ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
        List<Docket> docketList = new LinkedList<>();

        // 沒有分組
        if (swaggerProperties.getDocket().size() == 0) {
            final Docket docket = createDocket(swaggerProperties);
            configurableBeanFactory.registerSingleton("defaultDocket", docket);
            docketList.add(docket);
            return docketList;
        }

        // 分組創(chuàng)建
        for (String groupName : swaggerProperties.getDocket().keySet()) {
            SwaggerProperties.DocketInfo docketInfo = swaggerProperties.getDocket().get(groupName);

            ApiInfo apiInfo = new ApiInfoBuilder()
                    .title(docketInfo.getTitle().isEmpty() ? swaggerProperties.getTitle() : docketInfo.getTitle())
                    .description(docketInfo.getDescription().isEmpty() ? swaggerProperties.getDescription() : docketInfo.getDescription())
                    .version(docketInfo.getVersion().isEmpty() ? swaggerProperties.getVersion() : docketInfo.getVersion())
                    .license(docketInfo.getLicense().isEmpty() ? swaggerProperties.getLicense() : docketInfo.getLicense())
                    .licenseUrl(docketInfo.getLicenseUrl().isEmpty() ? swaggerProperties.getLicenseUrl() : docketInfo.getLicenseUrl())
                    .contact(
                            new Contact(
                                    docketInfo.getContact().getName().isEmpty() ? swaggerProperties.getContact().getName() : docketInfo.getContact().getName(),
                                    docketInfo.getContact().getUrl().isEmpty() ? swaggerProperties.getContact().getUrl() : docketInfo.getContact().getUrl(),
                                    docketInfo.getContact().getEmail().isEmpty() ? swaggerProperties.getContact().getEmail() : docketInfo.getContact().getEmail()
                            )
                    )
                    .termsOfServiceUrl(docketInfo.getTermsOfServiceUrl().isEmpty() ? swaggerProperties.getTermsOfServiceUrl() : docketInfo.getTermsOfServiceUrl())
                    .build();


            Docket docket = new Docket(DocumentationType.OAS_30)
                    .host(swaggerProperties.getHost())
                    .apiInfo(apiInfo)
                    .globalRequestParameters(assemblyGlobalOperationParameters(swaggerProperties.getGlobalOperationParameters(),
                            docketInfo.getGlobalOperationParameters()))
                    .groupName(groupName)
                    .select()
                    .apis(RequestHandlerSelectors.basePackage(docketInfo.getBasePackage()))
                    .paths(buildPredicateSelector(docketInfo))
                    .build()
                    .securitySchemes(securitySchemes())
                    .securityContexts(securityContexts());

            configurableBeanFactory.registerSingleton(groupName, docket);
            docketList.add(docket);
        }
        return docketList;
    }

    /**
     * 創(chuàng)建 Docket對象
     *
     * @param swaggerProperties swagger配置
     * @return Docket
     */
    private Docket createDocket(final SwaggerProperties swaggerProperties) {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title(swaggerProperties.getTitle())
                .description(swaggerProperties.getDescription())
                .version(swaggerProperties.getVersion())
                .license(swaggerProperties.getLicense())
                .licenseUrl(swaggerProperties.getLicenseUrl())
                .contact(new Contact(swaggerProperties.getContact().getName(),
                        swaggerProperties.getContact().getUrl(),
                        swaggerProperties.getContact().getEmail()))
                .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
                .build();

        SwaggerProperties.DocketInfo dockerInfo = new SwaggerProperties.DocketInfo();
        dockerInfo.setBasePackage(swaggerProperties.getBasePackage());
        dockerInfo.setExcludePath(swaggerProperties.getExcludePath());

        return new Docket(DocumentationType.OAS_30)
                .host(swaggerProperties.getHost())
                .apiInfo(apiInfo)
                .enable(swaggerProperties.isEnabled())
                .globalRequestParameters(buildGlobalOperationParametersFromSwaggerProperties(
                        swaggerProperties.getGlobalOperationParameters()))
                .select()
                .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
                .paths(buildPredicateSelector(dockerInfo))
                .build()
                .securitySchemes(securitySchemes())
                .securityContexts(securityContexts());
    }

    private List<SecurityContext> securityContexts() {
        List<SecurityContext> contexts = new ArrayList<>(1);
        SecurityContext securityContext = SecurityContext.builder()
                .securityReferences(defaultAuth())
                .build();
        contexts.add(securityContext);
        return contexts;
    }

    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> references = new ArrayList<>(1);
        references.add(new SecurityReference(AUTH_KEY, authorizationScopes));
        return references;
    }

    private List<SecurityScheme> securitySchemes() {
        List<SecurityScheme> apiKeys = new ArrayList<>(1);
        ApiKey apiKey = new ApiKey(AUTH_KEY, AUTH_KEY, "header");
        apiKeys.add(apiKey);
        return apiKeys;
    }

    /**
     * 生成swagger全局通用參數(shù) 如:請求token
     */
    private List<RequestParameter> buildGlobalOperationParametersFromSwaggerProperties(
            List<SwaggerProperties.GlobalOperationParameter> globalOperationParameters) {

        List<RequestParameter> parameters = Lists.newArrayList();
        if (Objects.isNull(globalOperationParameters)) {
            return parameters;
        }
        for (SwaggerProperties.GlobalOperationParameter globalOperationParameter : globalOperationParameters) {
            parameters.add(new RequestParameterBuilder()
                    //參數(shù)名稱
                    .name(globalOperationParameter.getName())
                    //參數(shù)說明
                    .description(globalOperationParameter.getDescription())
                    //參數(shù)存放位置
                    .in(globalOperationParameter.getParameterType())
                    //參數(shù)數(shù)據(jù)類型 暫時(shí)寫死為string 有其他需求再改
                    .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
                    //參數(shù)是否必須
                    .required(Boolean.parseBoolean(globalOperationParameter.getRequired()))
                    .build());
        }
        return parameters;
    }


    /**
     * 局部參數(shù)按照name覆蓋局部參數(shù)
     *
     * @param globalOperationParameters
     * @param docketOperationParameters
     * @return
     */
    private List<RequestParameter> assemblyGlobalOperationParameters(
            List<SwaggerProperties.GlobalOperationParameter> globalOperationParameters,
            List<SwaggerProperties.GlobalOperationParameter> docketOperationParameters) {

        if (Objects.isNull(docketOperationParameters) || docketOperationParameters.isEmpty()) {
            return buildGlobalOperationParametersFromSwaggerProperties(globalOperationParameters);
        }

        Set<String> docketNames = docketOperationParameters.stream()
                .map(SwaggerProperties.GlobalOperationParameter::getName)
                .collect(Collectors.toSet());

        List<SwaggerProperties.GlobalOperationParameter> resultOperationParameters = Lists.newArrayList();

        if (Objects.nonNull(globalOperationParameters)) {
            for (SwaggerProperties.GlobalOperationParameter parameter : globalOperationParameters) {
                if (!docketNames.contains(parameter.getName())) {
                    resultOperationParameters.add(parameter);
                }
            }
        }

        resultOperationParameters.addAll(docketOperationParameters);
        return buildGlobalOperationParametersFromSwaggerProperties(resultOperationParameters);
    }

    private java.util.function.Predicate<String> buildPredicateSelector(SwaggerProperties.DocketInfo config) {
        // 當(dāng)沒有配置任何path的時(shí)候,解析/**
        if (config.getBasePath().isEmpty()) {
            config.getBasePath().add("/**");
        }
        List<java.util.function.Predicate<String>> basePath = new ArrayList<>();
        for (String path : config.getBasePath()) {
            basePath.add(PathSelectors.ant(path));
        }

        // exclude-path處理
        List<java.util.function.Predicate<String>> excludePath = new ArrayList<>();
        for (String path : config.getExcludePath()) {
            excludePath.add(PathSelectors.ant(path));
        }

        // 當(dāng)沒有配置任何path的時(shí)候,解析/.*
        if (config.getBasePath().isEmpty() || config.getBasePath() == null) {
            return PathSelectors.any().and(excludePath.stream().reduce(each -> true, (a, b) -> a.and(b.negate())));
        }

        //組裝 base-path 和 exclude-path each為false原因是,如果是true,有任何or,都不會走右邊
        return basePath.stream().reduce(each -> false, Predicate::or)
                .and(excludePath.stream().reduce(each -> true, (a, b) -> a.and(b.negate())));

    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
}

1.3 springboot注定裝配配置
resources目錄下先建文件夾 META-INF
文件夾下創(chuàng)建 spring.factories 文件 ,內(nèi)容如下

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ly.tulip.commons.swagger.config.SwaggerAutoConfiguration

step1 做完,其他服務(wù)引入該公共模塊即整合了swagger3.0
1.3,1.4兩個(gè)文件可以根據(jù)需求搞一個(gè)簡單的配置,這個(gè)配置網(wǎng)上都有,官方文檔也有,我也是參考其他項(xiàng)目的,僅當(dāng)學(xué)習(xí)

step2:gateway聚合配置
2.1.引入step1 的模塊
2.2. 引入兩個(gè)核心配置文件

SwaggerProvider

import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

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

/**
 * 類說明: 聚合系統(tǒng)接口
 *
 * @author wqf
 * @date 2022/7/26 16:47
 */
@Component
@Primary
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider {

    // SWAGGER3默認(rèn)的URL后綴
    public static final String SWAGGER3URL = "/v3/api-docs";
    public static final String SWAGGER_VERSION = "3.0";

    // 網(wǎng)關(guān)路由
    @Autowired
    private RouteLocator routeLocator;

    @Autowired
    private GatewayProperties gatewayProperties;

    // 聚合其他服務(wù)接口
    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resourceList = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        // 獲取網(wǎng)關(guān)中配置的route
        routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));

        gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
                .forEach(routeDefinition -> routeDefinition.getPredicates().stream()
                        .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
                        .forEach(predicateDefinition -> {
                                    String id = routeDefinition.getId();
                                    String location = id + SWAGGER3URL;
                                    resourceList.add(swaggerResource(id, location));

                                }
                        ));
        return resourceList;
    }

    private SwaggerResource swaggerResource(String name, String location) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion(SWAGGER_VERSION);
        return swaggerResource;
    }
}

SwaggerHandler


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;

import java.util.Optional;

@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));
    }

    @SuppressWarnings("rawtypes")
    @GetMapping("")
    public Mono<ResponseEntity> swaggerResources() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
}

2.3 yml文件配置轉(zhuǎn)發(fā)路由

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  #配置網(wǎng)關(guān)路由規(guī)則 按服務(wù)名進(jìn)行路由
    gateway:
      routes:
        # 唯一標(biāo)識 - 系統(tǒng)核心服務(wù)
        - id: base-server
          uri: lb://base-server
          #斷言,路徑相匹配的進(jìn)行路由,配置服務(wù)端的方法路
          predicates:
            - Path=/base-server/**
          filters:
            - StripPrefix=0
        # 唯一標(biāo)識 - 系統(tǒng)用戶服務(wù)
        - id: auth-server
          uri: lb://auth-server
          predicates:
            - Path=/auth-server/**
          filters:
            - StripPrefix=0

以上基本配置完成,但是測試時(shí)發(fā)現(xiàn),接口調(diào)試時(shí)請求地址缺少轉(zhuǎn)發(fā)路由地址,解決配置如下


import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 類說明: 處理gateway集成knife4j后接口請求沒帶上服務(wù)名稱問題
 *
 * @author wqf
 * @date 2022/8/11 10:17
 */
@Slf4j
@Component
public class SwaggerGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path=exchange.getRequest().getPath().toString();
        if (!path.endsWith("/v3/api-docs")){
            return chain.filter(exchange);
        }
        String[] pathArray=path.split("/");
        String basePath=pathArray[1];
        ServerHttpResponse originalResponse = exchange.getResponse();
        // 定義新的消息頭
        HttpHeaders headers = new HttpHeaders();
        headers.putAll(exchange.getResponse().getHeaders());
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (Objects.equals(getStatusCode(), HttpStatus.OK) && body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                        List<String> list = new ArrayList<String>();
                        dataBuffers.forEach(dataBuffer -> {
                            byte[] content = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(content);
                            DataBufferUtils.release(dataBuffer);
                            list.add(new String(content, StandardCharsets.UTF_8));
                        });
                        String s = listToString(list);
                        int length = s.getBytes().length;
                        headers.setContentLength(length);
                        JSONObject jsonObject= JSONUtil.parseObj(s);
                        jsonObject.set("basePath",basePath);
                        s=jsonObject.toString();
                        return bufferFactory().wrap(s.getBytes());
                    }));
                }
                return super.writeWith(body);
            };
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(super.getHeaders());
                //由于修改了請求體的body,導(dǎo)致content-length長度不確定,因此使用分塊編碼
                httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
                httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                return httpHeaders;
            }

            private String listToString(List<String> list){
                StringBuilder stringBuilder=new StringBuilder();
                for (String s:list){
                    stringBuilder.append(s);
                }
                return stringBuilder.toString();
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    @Override
    public int getOrder() {
        return -2;
    }
}

注意點(diǎn):

  1. 網(wǎng)關(guān)對:/swagger-ui/**; /v3/api-docs 等swagger訪問路徑記得放行
  2. 網(wǎng)關(guān)配置文件需要配置路由規(guī)則,以下配置是按服務(wù)名稱進(jìn)行路由轉(zhuǎn)發(fā)的,好處是服務(wù)端口改了也沒事。

以上整合完畢,兩個(gè)訪問地址如下:

swagger 訪問地址:
swagger原生版:   http://ip:端口號/swagger-ui/index.html
knife4j界面美化版:http://ip:端口號/doc.html#/home

其他問題:
1.開啟配置增強(qiáng),一般生產(chǎn)環(huán)境不需要這個(gè)文檔的訪問
2.設(shè)置Ui默認(rèn)顯示語言為中文
3.開啟請求參數(shù)緩存
配置如下:

knife4j:
  #生產(chǎn)環(huán)境禁止訪問文檔
  production: true
  enable: true
  setting:
    #設(shè)置Ui默認(rèn)顯示語言為中文
    language: zh-CN
    #開啟請求參數(shù)緩存
    enableRequestCache: true

以上基本夠用了,有其他的需求再看看官方文檔吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • knife4j knife4j是為Java MVC框架集成Swagger生成Api文檔的增強(qiáng)解決方案,前身是swa...
    郭藝賓閱讀 22,065評論 3 13
  • 說明 最近被拉入一個(gè)項(xiàng)目組寫API,用到了一個(gè)Swagger的文檔框架,自動化文檔,有效減少前后端交流。這...
    阿武_Accat閱讀 12,241評論 0 2
  • Swagger整合Jwt授權(quán)配置 歡迎關(guān)注博主公眾號「Java大師」, 專注于分享Java領(lǐng)域干貨文章http:/...
    85年的大喇叭閱讀 2,027評論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,525評論 19 139
  • 目前swagger3.0已經(jīng)發(fā)布,功能更加強(qiáng)大,并且向下兼容,支持WebFlux,所以使用gateway時(shí)完美兼容...
    歸來_仍是少年閱讀 5,424評論 7 17

友情鏈接更多精彩內(nèi)容