美文网首页springboot
springboot 2.0 + springcloud ali

springboot 2.0 + springcloud ali

作者: JSM_SIMONS | 来源:发表于2019-05-31 17:48 被阅读0次

    前言:

    在实际开发当中前后端对接的时候,经常会用到swagger,但是如果使用了路由的话,如果不用路由网关去分发swagger的话,前后端对解决的时候会很麻烦,微服务的接口增多对于对接的成本就会变高,那么这一节就写一写gatewaty转发swagger

    代码配置:

    具体微服务

    需要用到swagger的微服务上添加swagger的依赖,为了方便我这边直接就使用上一章用到的那个test接口的nacos客户端的项目

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <groupId>com.gitee.simons.cloud</groupId>
            <artifactId>simons-cloud</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
    
        <modelVersion>4.0.0</modelVersion>
        <artifactId>simons-nacos</artifactId>
        <name>${project.artifactId} [web]</name>
        <packaging>jar</packaging>
    
        <!-- 版本,字段关键字 -->
        <properties>
            <start-class>com.gitee.simons.nacos.NacosApplication</start-class>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            </dependency>
    
            <!--swagger相关 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
        </dependencies>
    
    <profiles>
            <profile>
                <id>spring-boot</id>
                <build>
                    <plugins>
                        <plugin>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-maven-plugin</artifactId>
                            <version>${spring-boot.version}</version>
                            <configuration>
                                <mainClass>${start-class}</mainClass>
                            </configuration>
                            <executions>
                                <execution>
                                    <goals>
                                        <goal>repackage</goal>
                                    </goals>
                                </execution>
                            </executions>
                        </plugin>
                    </plugins>
                </build>
            </profile>
    
    
        </profiles>
    
    
        <build>
            <finalName>${project.artifactId}</finalName>
            <plugins>
                <!--解決持續集成無法找到main類 -->
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <mainClass>${start-class}</mainClass>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    主要就是加上swagger,由于swagger的配置版本越高,功能越全面这边建议无脑最新relase,反正不在生产环境上出现的东西,怎么方便怎么来。

    接下来就是要加上一些跨域和swagger的配置,直接复制我的代码即可

    swagger的配置类,要注意的是,return的docket里面有个basePackagex需要根据实际的包路径填写的
    复制的时候注意一下即可

    package com.gitee.simons.nacos.swagger.config;
    
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.ParameterBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.schema.ModelRef;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.Contact;
    import springfox.documentation.service.Parameter;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger.web.DocExpansion;
    import springfox.documentation.swagger.web.OperationsSorter;
    import springfox.documentation.swagger.web.UiConfiguration;
    import springfox.documentation.swagger.web.UiConfigurationBuilder;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * SwaggerConfig api文档地址:/swagger-ui.html
     *
     * @author JSM
     */
    
    @Configuration
    public class SwaggerConfig {
    
        @Value("${spring.application.name}")
        private String applicationName;
    
        @Bean
        public Docket createRestApi() {
            // 构造token给测试的时候填写
            ParameterBuilder tokenPar = new ParameterBuilder();
            List<Parameter> pars = new ArrayList<Parameter>();
            tokenPar.name("token").description("用户令牌(不需用户鉴权的不需要传)").modelRef(new ModelRef("string")).parameterType("header")
                    .required(false).build();
            pars.add(tokenPar.build());
    
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(defaultTitleInfo())
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.gitee.simons"))
                    .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                    .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                    .paths(PathSelectors.any())
                    .build().globalOperationParameters(pars);
        }
    
        /**
         * 默认标题信息
         *
         * @return
         */
        private ApiInfo defaultTitleInfo() {
            // 大标题
            return new ApiInfoBuilder().title("")
                    // 详细描述
                    .description("")
                    .contact(new Contact("", "", ""))
                    // 版本
                    .version("4.0")
                    .build();
        }
    
        @Bean
        UiConfiguration uiConfig() {
            return UiConfigurationBuilder.builder().docExpansion(DocExpansion.LIST).operationsSorter(OperationsSorter.ALPHA).build();
        }
    }
    

    swagger的资源过滤配置

    package com.gitee.simons.nacos.swagger.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    /**
     * test
     *
     * @author: jsm
     * @date: 2018/11/16 10:40
     *
     */
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        /**
         * 跨域支持
         * @param registry
         */
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowCredentials(true)
                    .allowedMethods("*")
                    .maxAge(3600)
                    .allowedHeaders("X-Requested-With", "Content-Type","token");
        }
    
        /**
         * 添加静态资源--过滤swagger-api (开源的在线API文档)
         * @param registry
         */
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/js/**").addResourceLocations("classpath:/js/");
            registry.addResourceHandler("swagger-ui.html")
                    .addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**")
                    .addResourceLocations("classpath:/META-INF/resources/webjars/");
    
        }
    
    }
    
    

    全局跨域配置

    package com.gitee.simons.nacos.swagger.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class CrosAppConfig implements WebMvcConfigurer {
        
        @Autowired
        private CrosInterceptor crosInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // 配置全局 的cros
            registry.addInterceptor(crosInterceptor).addPathPatterns("/**/**");
        }
    
    
    
    }
    
    

    跨域的详细信息设置

    package com.gitee.simons.nacos.swagger.config;
    
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @Component
    public class CrosInterceptor extends HandlerInterceptorAdapter {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            //处理跨域名问题
            response.setHeader("Access-Control-Allow-Origin", "*");  
            response.setHeader("Access-Control-Allow-Methods", "*");  
            response.setHeader("Access-Control-Max-Age", "3600");    
            response.setHeader("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,token");  
            response.setHeader("Access-Control-Allow-Credentials", "true");
            return true;
        }
    }
    

    那么只要复制了这些文件之后,在applicaiton文件上面加上@EnableSwagger2标签就可以的了

    package com.gitee.simons.nacos;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.context.annotation.ComponentScan;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    @ComponentScan("com.gitee")
    @EnableSwagger2
    public class NacosApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(NacosApplication.class, args);
        }
    }
    
    

    配置完这些信息之后,启动application在浏览器输入http://localhost:8112/swagger-ui.html
    就可以直接请求到swagger的页面

    image.png

    gateway转发配置

    这一步之后具体的微服务就可以实现请求了,但是我们的目标是需要可以做到gateway转发, 那么应该还要再gateway项目下做一些操作

    如果用过zuul做过路由转发的朋友,应该知道zuul是用的路由页面跳转的方式,这种方式得在serlvet的方式下才可以进行,但gateway是一个用netty框架为基础的服务,那么他就无法使用zuul的方式进行路由,但可以使用另外的方式,就是获取gateway路由参数中的服务,然后显示在select a spec上,通过请求swagger的v2/api-docs接口返回json数据,再放进swagger的页面中,大概这种思路

    那么直接上配置

    首先给gateway加上swagger的pom 依赖

            <!--swagger相关 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
    

    对swagger路由的配置

    package com.sunmnet.mediaroom.gateway.config;
    
    import lombok.AllArgsConstructor;
    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.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;
    
    /**
     * @author Sywd
     */
    @Component
    @Primary
    @AllArgsConstructor
    public class SwaggerProvider implements SwaggerResourcesProvider {
        public static final String API_URI = "/v2/api-docs";
        private final RouteLocator routeLocator;
        private final GatewayProperties gatewayProperties;
    
    
        @Override
        public List<SwaggerResource> get() {
            List<SwaggerResource> resources = new ArrayList<>();
            List<String> routes = new ArrayList<>();
            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 -> resources.add(swaggerResource(routeDefinition.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;
        }
    }
    
    

    因为swagger打开页面的时候,会默认请求一系列的请求,这些请求会返回页面信息的基本构成内容,
    但由于gateway上用的是webflux,swagger是无法获取到这些信息的,那么我们只能自己伪造一个这些接口并返回成功的空信息,以此绕过报错,然后具体资源信息通过转发的请求各个微服务中获取

    swagger资源报错信息绕过

    package com.gitee.simons.gateway.config;
    
    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.*;
    
    import java.util.Optional;
    
    @RestController
    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("/swagger-resources/configuration/security")
        public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
            return Mono.just(new ResponseEntity<>(
                    Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
        }
    
        @GetMapping("/swagger-resources/configuration/ui")
        public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
            return Mono.just(new ResponseEntity<>(
                    Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
        }
    
        @GetMapping("/swagger-resources")
        public Mono<ResponseEntity> swaggerResources() {
            return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
        }
    
        @GetMapping("/")
        public Mono<ResponseEntity> swaggerResourcesN() {
            return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
        }
    
        @GetMapping("/csrf")
        public Mono<ResponseEntity> swaggerResourcesCsrf() {
            return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
        }
    }
    
    

    配置完之后,就可以启动application,请求通过swagger路由获取到具体的信息了

    image.png

    相关文章

      网友评论

        本文标题:springboot 2.0 + springcloud ali

        本文链接:https://www.haomeiwen.com/subject/essgtctx.html