美文网首页springboot收藏
Springboot 升级带来的Swagger异常

Springboot 升级带来的Swagger异常

作者: 程序员札记 | 来源:发表于2022-05-10 11:20 被阅读0次

    当升级到Springboot 2.6.0 以上的版本后,Swagger 就不能正常工作了, 启动时报如下错误。当然如果你再使用sping boot ActuatorSpringfox, 也会引起相关的NPE error. (github issue: https://github.com/springfox/springfox/issues/3462)

    NFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: Caused by: org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at java.lang.Iterable.forEach(Iterable.java:75)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: at com.ebay.adscollection.app.AdstrackingcollectionApplication.main(AdstrackingcollectionApplication.java:12)
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: ... 14 more
    INFO | jvm 1 | 2022/04/27 21:47:05 | WrapperJarApp: Caused by: java.lang.NullPointerException
    
    

    下面就一一说明怎么解决

    路径匹配策略

    报错原因:
    springboot2.6.0中将SpringMVC 默认路径匹配策略从AntPathMatcher 更改为PathPatternParser,导致出错

    解决:
    在 application.properties 文件中指定spring.mvc.pathmatch.matching-strategy=ant_path_matcher

    Actuator

    在Springboot 2.6 里Actuator 指定了ant_path_matcher, 并且没有办法通过配置的方式来切换。 我们可以通过定义bean 的方式来实现强制mapping。

    @Bean
    public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {
        List<ExposableEndpoint<?>> allEndpoints = new ArrayList();
        Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
        allEndpoints.addAll(webEndpoints);
        allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
        allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
        String basePath = webEndpointProperties.getBasePath();
        EndpointMapping endpointMapping = new EndpointMapping(basePath);
        boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
        return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
    }
    
    
    private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
        return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
    }
    
    

    springfox

    Springfox-swagger2:jar:3.0.0 在 spring boot 2.6.x不在支持,而是转向springdoc
    因此就在springboot 2.6.x , spring doc 应该去掉,而是用 springdoc-openapi-ui 替换。

       <dependency>
          <groupId>org.springdoc</groupId>
          <artifactId>springdoc-openapi-ui</artifactId>
          <version>1.6.8</version>
       </dependency>
    

    替换掉 swagger 2 annotations ,选用wagger 3 annotations wagger 3 annotations 报名是 io.swagger.v3.oas.annotations.

    @Api → @Tag
    
    @ApiIgnore → @Parameter(hidden = true) or @Operation(hidden = true) or @Hidden
    
    @ApiImplicitParam → @Parameter
    
    @ApiImplicitParams → @Parameters
    
    @ApiModel → @Schema
    
    @ApiModelProperty(hidden = true) → @Schema(accessMode = READ_ONLY)
    
    @ApiModelProperty → @Schema
    
    @ApiOperation(value = "foo", notes = "bar") → @Operation(summary = "foo", description = "bar")
    
    @ApiParam → @Parameter
    
    @ApiResponse(code = 404, message = "foo") → @ApiResponse(responseCode = "404", description = "foo")
    

    如果你在application 里,不是用springfox 的dependency, 而是自己定义多个Docket

    @Bean
      public Docket publicApi() {
          return new Docket(DocumentationType.SWAGGER_2)
                  .select()
                  .apis(RequestHandlerSelectors.basePackage("org.github.springshop.web.public"))
                  .paths(PathSelectors.regex("/public.*"))
                  .build()
                  .groupName("springshop-public")
                  .apiInfo(apiInfo());
      }
    
      @Bean
      public Docket adminApi() {
          return new Docket(DocumentationType.SWAGGER_2)
                  .select()
                  .apis(RequestHandlerSelectors.basePackage("org.github.springshop.web.admin"))
                  .paths(PathSelectors.regex("/admin.*"))
                  .apis(RequestHandlerSelectors.withMethodAnnotation(Admin.class))
                  .build()
                  .groupName("springshop-admin")
                  .apiInfo(apiInfo());
      }
    
    

    那么就要改成

    @Bean
      public GroupedOpenApi publicApi() {
          return GroupedOpenApi.builder()
                  .group("springshop-public")
                  .pathsToMatch("/public/**")
                  .build();
      }
      @Bean
      public GroupedOpenApi adminApi() {
          return GroupedOpenApi.builder()
                  .group("springshop-admin")
                  .pathsToMatch("/admin/**")
                  .addMethodFilter(method -> method.isAnnotationPresent(Admin.class))
                  .build();
      }
    
    

    如果只有一个Docket, 则要改成如下,用OpenAPI 而不是GroupedOpenApi

    @Bean
      public OpenAPI springShopOpenAPI() {
          return new OpenAPI()
                  .info(new Info().title("SpringShop API")
                  .description("Spring shop sample application")
                  .version("v0.0.1")
                  .license(new License().name("Apache 2.0").url("http://springdoc.org")))
                  .externalDocs(new ExternalDocumentation()
                  .description("SpringShop Wiki Documentation")
                  .url("https://springshop.wiki.github.org/docs"));
      }
    
    

    同时设置参数在spring.factories 文件

    springdoc.packagesToScan=package1, package2
    springdoc.pathsToMatch=/v1, /api/balance/**
    
    

    其他

    If the swagger-ui is served behind a proxy:

    To customise the Swagger UI:

    To hide an operation or a controller from documentation:

    refer link:

    相关文章

      网友评论

        本文标题:Springboot 升级带来的Swagger异常

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