Knife4j+gateway动态路由聚合微服务API

2023-11-11

概述

knife4j的聚合API,其实官网有现成的例子,但包括其他能搜索到的资料都是基于静态网关的配置,我们现有的都是结合nacos实现动态网关配置,基于此留下这篇完整的教程文档
说明:本文假定你有一定的springcloud等相关知识,如没有请自行查找其他资料

基础环境

spring-boot-dependencies2.2.9.RELEASE
spring-cloud-dependenciesHoxton.SR7
spring-cloud-alibaba-dependencies2.2.3.RELEASE
knife4j2.0.9

代码

父parent

<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>${spring-boot-dependencies.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud-dependencies.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>${spring-cloud-alibaba-dependencies.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
       <!-- knife4j提供的微服务starter -->
      <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-spring-boot-starter</artifactId>
        <version>${knife4j.version}</version>
      </dependency>
      <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-micro-spring-boot-starter</artifactId>
        <version>${knife4j.version}</version>
      </dependency>
   </dependencyManagement>

springcloud 网关

pom

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

    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
        <!-- knife4j提供的微服务starter -->
    <dependency>
      <groupId>com.github.xiaoymin</groupId>
      <artifactId>knife4j-spring-boot-starter</artifactId>
    </dependency>
 </dependencies>

相关配置

  1. 动态路由配置,一般配置在nacos上,选择json格式
[
  {
    "id": "xxx",
    "order": 1,
    "predicates": [
      {
        "args": {
          "pattern": "/api/author/**"
        },
        "name": "Path"
      }
    ],
    "uri": "lb://author-service"
  }
]
  1. 网关配置,建议放在application.yml里面
spring:
  cloud:
    gateway:
      httpclient:
        # 全局的TCP连接超时时间默认时间是45秒,网络故障的时候,连接时间要等待45秒,而网络连接是同步阻塞
        connect-timeout: 2000
        # 全局的响应超时时间,网络链接后,后端服务多久不返回网关就报错 The response timeout.
        response-timeout: PT30S
        pool:
          # scg的空闲连接超时回收时间
          max-idle-time: PT1S
        websocket:
          # 1024*1024*10  上传的文件大小
          max-frame-payload-length: 10485760
      # 全局跨域配置
      #globalcors:
        #cors-configurations:
          #'[/**]': #匹配所有请求
            #allowedOrigins: "*" #跨域处理 允许所有的域
            #allowedMethods: # 支持的方法
              #- GET
              #- POST
              #- PUT
              #- DELETE
      # nacos 服务发现
      discovery:
        locator:
          lowerCaseServiceId: true
          enabled: true
      # 配置全局路由
      #default-filters:
        # 开发环境重写路由规则 该配置会影响到灰度组件,如果开启需要关闭灰度组件
        #- DevLocalRewriteRouteFilter=true
        # 签名校验
        #- SecretGatewayFilter=true

动态路由代码实现

  1. DynamicRouteService 动态路由管理服务
import org.springframework.cloud.gateway.route.RouteDefinition;

/**
 * TODO  NacosDynamicRutesService <br>
 *
 * @date: 2020/12/7 <br>
 * @author: xiaolinlin <br>
 * @since: 1.0 <br>
 * @version: 1.0 <br>
 */
public interface DynamicRouteService {

    /**
     * 新增一个route定义
     *
     * @param routeDefinition : 路由信息
     * @param manual: 控制台-手动
     * @return java.lang.String
     * @author xiaolinlin
     * @date 2020/12/7
     **/
    String addRoute(RouteDefinition routeDefinition,boolean manual);

    /**
     * 更新一个route定义
     *
     * @param routeDefinition :
     * @return java.lang.String
     * @author xiaolinlin
     * @date 2020/12/7
     **/
    String updateRoute(RouteDefinition routeDefinition);

    /**
     * 通过ID删除一个route定义
     *
     * @param id :
     * @return java.lang.String
     * @author xiaolinlin
     * @date 2020/12/7
     **/
    String deleteRoute(String id);
  1. AbstractDynamicRouteService基类实现
import com.alibaba.fastjson.JSONObject;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.http.ResponseEntity;
import reactor.core.publisher.Mono;

/**
 * 动态路由配置抽象类<br>
 *
 * @date: 2020/12/23 <br>
 * @author: xiaolinlin <br>
 * @since: 1.0 <br>
 * @version: 1.0 <br>
 */
@Slf4j
public abstract class AbstractDynamicRouteService implements DynamicRouteService, ApplicationEventPublisherAware,
    DisposableBean {

    @Autowired
    protected RouteDefinitionWriter routeDefinitionWriter;

    protected ApplicationEventPublisher applicationEventPublisher;


    /**
     * 路由列表  TODO  分布式配置
     */
    protected static final List<String> ROUTE_LIST = new ArrayList<>();

    /**
     * 新增一个route定义
     *
     * @param routeDefinition :
     * @param manual: 是否手动配置
     * @return java.lang.String
     * @author xiaolinlin
     * @date 2020/12/7
     **/
    @Override
    public String addRoute(RouteDefinition routeDefinition, boolean manual) {
        try {
            log.debug("新增route配置:{}", JSONObject.toJSONString(routeDefinition));
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            ROUTE_LIST.add(routeDefinition.getId());
            if (manual) {
                // 手动发布,每添加一次更新一次
                publish();
            }
        } catch (Exception e) {
            log.error("添加路由失败,当前路由信息:{},错误详情:{}", JSONObject.toJSONString(routeDefinition), e);
        }
        return "success";
    }

    /**
     * 更新一个route定义
     *
     * @param routeDefinition :
     * @return java.lang.String
     * @author xiaolinlin
     * @date 2020/12/7
     **/
    @Override
    public String updateRoute(RouteDefinition routeDefinition) {
        if (null == routeDefinition) {
            return "fail";
        }
        log.debug("更新route配置:{}", JSONObject.toJSONString(routeDefinition));
        String id = routeDefinition.getId();
        try {
            this.routeDefinitionWriter.delete(Mono.just(id));
            this.routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            publish();
        } catch (Exception e) {
            log.error("更新路由信息失败,当前ID:{},错误信息:{}", id, e);
            return "fail";
        }
        return "success";
    }

    /**
     * 通过ID删除一个route定义
     *
     * @param id :
     * @return java.lang.String
     * @author xiaolinlin
     * @date 2020/12/7
     **/
    @Override
    public String deleteRoute(String id) {
        if (StringUtils.isEmpty(id)) {
            return "fail";
        }
        log.debug("触发了删除route配置,路由ID: {}", id);
        try {
            this.routeDefinitionWriter.delete(Mono.just(id))
                .then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
                .onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build()))
                .subscribe();
            return "success";
        } catch (Exception e) {
            log.error("删除路由失败,当前路由ID:{},异常原因:{}", id, e);
            return "fail";
        }
    }

    /**
     * 初始化路由配置
     *
     * @param config :
     * @return void
     * @author xiaolinlin
     * @date 2020/12/7
     **/
    protected void initConfig(String config) {
        if (StringUtils.isEmpty(config)) {
            return;
        }
        List<RouteDefinition> gatewayRouteDefinitions = JSONObject
            .parseArray(config, RouteDefinition.class);
        for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
            addRoute(routeDefinition, false);
        }
        // 重新发布更新
        publish();
    }

    /**
     * 发布重新更新
     *
     * @return void
     * @author xiaolinlin
     * @date 2020/12/7
     **/
    protected void publish() {
        log.info("当前网关路径节点:{}", JSONObject.toJSONString(ROUTE_LIST));
        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
    }

    /**
     * clearRoute
     *
     * @author xiaolinlin
     * @date 2020/12/7
     **/
    protected void clearRoute() {
        for (String id : ROUTE_LIST) {
            this.deleteRoute(id);
        }
        ROUTE_LIST.clear();
    }

    /**
     * Set the ApplicationEventPublisher that this object runs in.
     * <p>Invoked after population of normal bean properties but before an init
     * callback like InitializingBean's afterPropertiesSet or a custom init-method. Invoked before ApplicationContextAware's
     * setApplicationContext.
     *
     * @param applicationEventPublisher event publisher to be used by this object
     */
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    @Override
    public void destroy() throws Exception {
        ROUTE_LIST.clear();
    }
}
  1. nacos实现
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import xxx.AbstractDynamicRouteService;
import java.util.Properties;
import java.util.concurrent.Executor;
import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;

/**
 * 动态网关调整<br>
 *
 * @date: 2020/12/7 <br>
 * @author: xiaolinlin <br>
 * @since: 1.0 <br>
 * @version: 1.0 <br>
 */
@Service
@RefreshScope
@Slf4j
public class NacosDynamicRoutesImpl extends AbstractDynamicRouteService {

    // nacos 的配置信息
    /// private NacosConfigProperties nacosConfigProperties;

    /**
     * nacos服务地址
     *
     *
     *
     *
     *
     */
    @Value("${spring.cloud.nacos.server-addr}")
    private String serverAddr;
    /**
     * namespace
     */
    @Value("${spring.cloud.nacos.discovery.namespace}")
    private String nameSpace;

    /**
     * 配置组
     */
    @Value("${spring.cloud.nacos.config.discovery.group:DEFAULT_GROUP}")
    private String gourpId;

    /**
     * 动态路由dataID,JSON格式, 重点关注这里,nacos上的名称
     */
    @Value("${spring.cloud.nacos.config.route-data-id:xiaogj-youli-platform-gateway-route}")
    private String routeDataId;

    /**
     * 初始化启动监听nacos配置
     *
     * @return void
     * @author xiaolinlin
     * @date 2020/12/7
     **/
    @PostConstruct
    public void dynamicRouteByNacosListener() {
        log.info("gateway route init...");
        try {
            Properties properties = new Properties();
            properties.setProperty("serverAddr", serverAddr);
            properties.setProperty("namespace", nameSpace);
            ConfigService configService = NacosFactory.createConfigService(properties);
            String config = configService.getConfig(routeDataId, gourpId, 5000);
            // 初次初始化
            initConfig(config);
            // 动态更新
            configService.addListener(routeDataId, gourpId, new Listener() {
                @Override
                public Executor getExecutor() {
                    return null;
                }

                @Override
                public void receiveConfigInfo(String configInfo) {
                    clearRoute();
                    try {
                        log.info("配置发生了变更,变更网关配置内容:{}", configInfo);
                        initConfig(configInfo);
                    } catch (Exception e) {
                        log.warn("本次配置更新失败,错误详情:", e);
                    }
                }
            });
        } catch (NacosException e) {
            log.error("监听网关路由配置异常,错误信息:", e);
        }
    }
}

swagger 代码部分

  1. SwaggerHandler
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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;

/**
 * swagger <br>
 *
 * @date: 2021/9/22 <br>
 * @author: llxiao <br>
 * @since: 1.0 <br>
 * @version: 1.0 <br>
 */
@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)));
    }
}
  1. SwaggerProvider 主要是用于适配nacos动态路由
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import reactor.core.publisher.Flux;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

/**
 * 聚合系统接口<br>
 *
 * @date: 2021/9/22 <br>
 * @author: llxiao <br>
 * @since: 1.0 <br>
 * @version: 1.0 <br>
 */
@Slf4j
@Component
@Primary
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider, WebFluxConfigurer {

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

    /**
     * 网关路由
     */
    ///@Autowired
    ///private RouteLocator routeLocator;

    /**
     * yml方式获取路由服务
     */
    ///@Autowired
    ///private GatewayProperties gatewayProperties;

    /**
     * 使用动态路由,获取到指定的路由规则
     */
    @Autowired
    private RouteDefinitionRepository routeDefinitionRepository;

    /**
     * 聚合其他服务接口
     *
     * @return java.util.List<springfox.documentation.swagger.web.SwaggerResource>
     * @author llxiao
     * @date 2022/3/30
     **/
    @Override
    public List<SwaggerResource> get() {

        //接口资源列表
        List<SwaggerResource> resourceList = new ArrayList<>();

        //服务名称列表
        Set<String> routeHosts = new HashSet<>();

        // 去重,多负载服务只添加一次
        Set<String> existsServer = new HashSet<>();

        // 动态网关方式获取路由  nacos动态网关
        Flux<RouteDefinition> routeDefinitions = routeDefinitionRepository.getRouteDefinitions();
        routeDefinitions.subscribe(routeDefinition -> {
            List<PredicateDefinition> predicates = routeDefinition.getPredicates();
            if (CollectionUtil.isNotEmpty(predicates)) {
                String host = routeDefinition.getUri().getHost();
                routeHosts.add(host);
                String url = "/" + host + SWAGGER2URL;
                if (!existsServer.contains(url)) {
                    existsServer.add(url);
                    SwaggerResource swaggerResource = new SwaggerResource();
                    swaggerResource.setUrl(url);
                    swaggerResource.setName(host);
                    resourceList.add(swaggerResource);
                }

            }
        });
        log.info("网关服务地址列表:{}", JSON.toJSONString(routeHosts));
        return resourceList;
    }

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

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        /** swagger-ui 地址 */
        registry.addResourceHandler("/swagger-ui/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
        //favicon.ico
        registry.addResourceHandler("/favicon.ico")
            .addResourceLocations("classpath:/static/");
    }
}
  1. SwaggerHeaderFilter 可选,按需添加
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;

/**
 * SwaggerHeaderFilter <br>
 *
 * @date: 2022/3/31 <br>
 * @author: llxiao <br>
 * @since: 1.0 <br>
 * @version: 1.0 <br>
 */
@SuppressWarnings("ALL")
@Configuration
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {

    private static final String HEADER_NAME = "X-Forwarded-Prefix";
    private static final String URI = "/v2/api-docs";

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();

            if (StringUtils.endsWithIgnoreCase(path, URI)) {
                String basePath = path.substring(0, path.lastIndexOf(URI));
                ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
                ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
                return chain.filter(newExchange);
            } else {
                return chain.filter(exchange);
            }
        };
    }
}

微服务Knife4j引用公共组件

  1. pom
<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>

    <dependency>
      <groupId>com.github.xiaoymin</groupId>
      <artifactId>knife4j-spring-boot-starter</artifactId>
    </dependency>
  </dependencies>
  1. 代码部分
    2.1. Knife4jConfig 相关配置
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * Knife4jConfig <br>
 *
 * @date: 2021/11/26 <br>
 * @author: llxiao <br>
 * @since: 1.0 <br>
 * @version: 1.0 <br>
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "knife.swagger")
public class Knife4jConfig {

    private String version;
    private String basePackage;
    private String title;
    private String description;
    private String contactName;
    private Boolean enabled;
    private String termsOfServiceUrl = "https://xxx.com";

}

2.2. Knife4jConfiguration 核心实现

import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

/**
 * Knife4jConfiguration <br>
 *
 * @date: 2021/11/26 <br>
 * @author: llxiao <br>
 * @since: 1.0 <br>
 * @version: 1.0 <br>
 */
@Configuration
@ComponentScan(basePackages = "com.xx.swagger")
@EnableSwagger2WebMvc
@Slf4j
public class Knife4jConfiguration {

    @Bean(value = "defaultApi2")
    public Docket defaultApi2(Knife4jConfig knife4jConfig) {
        // 自定义统一错误码返回 -需要自行修改,仅供参考
        List<ResponseMessage> responseMessageList = new ArrayList<>();
        for (XXXCommonExceptionCode value : XXXCommonExceptionCode.values()) {
            ResponseMessage build = new ResponseMessageBuilder().code(value.getCode()).message(value.getMsg()).responseModel(
                new ModelRef(value.getMsg())).build();
            responseMessageList.add(build);
        }

        // 自定义通用header 定义  -需要自行修改,仅供参考
        List<Parameter> parameters = new ArrayList<>();
        parameters.add(new ParameterBuilder()
            .name("Auth-Token")
            .description("我是描述")
            .modelRef(new ModelRef("string"))
            .parameterType("header")
            .required(false)
            .build());
        log.info("Knife4j 加载完成....");
        return new Docket(DocumentationType.SWAGGER_2)
            // 自定义错误码
            .globalResponseMessage(RequestMethod.GET, responseMessageList)
            .globalResponseMessage(RequestMethod.POST, responseMessageList)
            .globalResponseMessage(RequestMethod.PUT, responseMessageList)
            .globalResponseMessage(RequestMethod.DELETE, responseMessageList)
            // header参数定义
            .globalOperationParameters(parameters)
            .apiInfo(new ApiInfoBuilder()
                .title(knife4jConfig.getTitle())
                .description(knife4jConfig.getDescription())
                .termsOfServiceUrl(knife4jConfig.getTermsOfServiceUrl())
                .contact(new Contact(knife4jConfig.getContactName(), "", ""))
                .version(knife4jConfig.getVersion())
                .build())
            //分组名称
            // .groupName(knife4jConfig.getVersion())
            .select()
            //这里指定Controller扫描包路径
            .apis(RequestHandlerSelectors.basePackage(knife4jConfig.getBasePackage()))
            .paths(PathSelectors.any())
            .build();
    }
}

2.2.3 EnabledKnife4j 使用注解开启

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

/**
 * EnabledCustomSwagger2 <br>
 *
 * @date: 2022/3/30 <br>
 * @author: llxiao <br>
 * @since: 1.0 <br>
 * @version: 1.0 <br>
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Knife4jConfiguration.class})
public @interface EnabledKnife4j {

}

2.2.4 配置参考

 knife:
   swagger:
     version: 1.0
     basePackage: com.xx.controller
     title: 标题
     description: 描述
     contactName: 联系人
     enabled: true

2.2.5 微服务端引入上述代码和配置,并在主启动类中添加@EnabledKnife4j注解即可

打开使用

http://gateway网关地址/doc.htm

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Knife4j+gateway动态路由聚合微服务API 的相关文章

随机推荐

  • Netlist与RTL仿真结果不匹配

    Netlist 仿真和RTL比匹配主要是因为Netlist会存在不定态以及传播不定态 导致仿真不正确 不定态的来源 大多数设计在上电时不能保证cell的特定的逻辑状态 flip flop或memory根据环境因素 芯片间的工艺差异等可能会有
  • nacos访问地址无登录界面问题:版本2.2.3

    场景 访问 localhost 8848 nacos 地址时 没有弹出登录界面 提示 当前集群没有开启鉴权 请参考文档开启鉴权 此时左边菜单也没有权限控制模块 原因分析 这是新版本的配置文件中鉴权开关没有开启的原因 具体可以修改以下配置项
  • python-使用chinese_calendar判断是否节假日/工作日/调休日,-使用weekday()判断星期几

    最近在编写一个小程序时 需要判断是否为工作日 节假日以及星期几 发现chinese calendar这个第三方插件还蛮好用 可以用来识别是否工作日 datetime下的weekday 判断星期几 现做如下分享 一 工作日 节假日的判断 1
  • 区块链技术学习笔记——1.区块链介绍

    基本概念 区块链介绍 区块链是一个由多个参与方共同维护的并且持续增长的分布式数据库 通常也被称作分布式共享账本 可以促进在业务网络中记录交易和跟踪资产的过程 把多笔交易的信息以及表明该区块的信息打包放在一起 经验证后的这个包就是区块 每个区
  • vue常用插件集合

    vue常用插件集合 vue treeselect vue treeselect 是一个多选组件 具有对 Vue js嵌套选项支持 vue layer使用方法 官网 可拖动对话框 支持传入组件 element表单自动生成 Element Pl
  • Go结构体

    struct类型是值类型 Go语言没有class类型 只有struct类型 定义 var stu Student var stu Student new Student var stu Student Student golang中的str
  • 码农保命秘诀之全身检查_专属全身体检项目大全

    废话不多说 狗命要紧 下面直接上干货 健康活着才有输出 特别是28 35之间的不注重身体 往往打败你的不是敌人 而是你自己 还怎么冲刺35大关 里面的注释简单看看就行 主要是照单让医生开单检查 挂个内科 说自己身体哪里不舒服 需要这些检查
  • ADS系列 – 低噪声放大器(LNA)模型下载安装及 LNA仿真设计

    1 概述 低噪声放大器 Low Noise Amplifier LNA 作为接收系统的第一个电路单元 它的性能直接影响着整个接收机的性能 低噪声放大器的功能是在保证产生最低的噪声前提下 将信号进行放大 以降低后续模块所产生的噪声对信号的影响
  • windows2008 不能跳出修复选项

    windows2008 R2意外断电后无法启动 提示修复或者正常启动 但即便你选择正常启动 系统还是会跳到修复选项 看起来是要折腾一下 BIOS自检 找到硬盘引导记录MBR MBR读分区表 找到主分区 读取主分区的引导记录 搜索启动管理器文
  • 三、C++语言进阶:动态库与静态库之类篇

    3 动态库与静态库之类篇 3 1 素材 文件 test h pragma once class Test public Test Test void Func int i test cpp include
  • 【死磕 Java 基础】— 我同事一个 select 分页语句查出来了 3000W 条数据

    大家好 我是大明哥 某天我正在工位上听着 Victory 愉快地敲着 hello world 这感觉就像我写的代码能征服世界 突然运维给我打了一个电话 说我们某台服务器 OOM 了 要我过去看下 这感觉就像 xxx 你懂的 去运维室 登录服
  • 【HBZ分享】ElasticSearch读写数据的原理 以及 路由策略原理-【短而精悍,易于理解】

    ElasticSearch分布式工作原理 是如何进行读写数据的 1 简单介绍ES 首先es是分布式的 只要我们启动相应数量的节点 并且这些节点分配相同的cluster name 那他们就对属于同一个集群了 创建索引的时候 只需要指定对应的主
  • js的继承的实现与代码编写

    js的继承的实现与代码编写 一 继承 对象的一个类可以从现有的类中派生 并且拥有现有的类的方法或是属性 这和过程叫做继承 被继承的类叫做父类或是基类 继承的类叫做子类 一个对象拥有另一个对象的属性和方法 在 JavaScript 中 是没有
  • QVariant类及QVariant与自定义数据类型转换的方法

    这个类型相当于是Java里面的Object 它把绝大多数Qt提供的数据类型都封装起来 起到一个数据类型 擦除 的作用 比如我们的 table单元格可以是string 也可以是int 也可以是一个颜色值 那么这么多类型怎么返回呢 于是 Qt提
  • 性能测试连载 (10)-数据错误率分析

    概述 性能测试脚本跑完了之后 我们除了要收集瓶颈数据 还有分析错误数据 通常一套脚本跑完 错误类型不止一种 但是jmeter只会在聚合报告里面给出一个总体的错误率 错误率 jmeter里的错误率是如何统计的 在返回的数据里面 只要succe
  • Maven项目中出现红色波浪线的解决过程

    一 问题分类 对于Maven项目出现红色波浪线可以首先对报错情况进行分析 我遇到的两种表现形式 还有其他的形式 请多多指教 形式一 所用文件均出现红色波浪线 此问题可能是配置问题 形式二 maven项目 Plugins Dependenci
  • Element-Ui

    安装环境搭建 Element Ul是饿了么前端团队推出的一款基于Vue js 2 0 的桌面端UI框架 一套为开发者 设计师和产品经理准备的基于 Vue 2 0 的桌面端组件库 手机端有对应框架是Mint UI 中文文档 http elem
  • 蓝桥杯 奇数倍数

    蓝桥杯 奇数倍数 题目 请你找到最小的整数 X 同时满足 1 X 是 2019 的整倍数 2 X 的每一位数字都是奇数 思路 1 先找2019倍数 2 判断它的每一位是否是奇数 Java代码 public class 奇数倍数 public
  • 2022跨年烟花代码(一)Canvas绘制3D烟花动画特效

    Canvas绘制3D烟花动画特效 html代码
  • Knife4j+gateway动态路由聚合微服务API

    概述 knife4j的聚合API 其实官网有现成的例子 但包括其他能搜索到的资料都是基于静态网关的配置 我们现有的都是结合nacos实现动态网关配置 基于此留下这篇完整的教程文档 说明 本文假定你有一定的springcloud等相关知识 如