繁体   English   中英

Spring 引导 - Swagger 文档不起作用

[英]Spring Boot - Swagger Documentation doesn't work

我有一个 REST API 项目构建和 Spring 引导,我想记录我所有的端点。 我已经实施了 swagger 来完成它并且成功了,但是最近我的项目不再运行了,配置与创建项目时的配置相同并且 Swagger 是有效的。

尝试运行项目时出现此错误:

WARN 17868 --- [  restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
....
ERROR 17868 --- [  restartedMain] o.s.boot.SpringApplication               : Application run failed

org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException

我究竟做错了什么?

这是我的项目配置:

主要的

package com.red.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
@PropertySource(value = "classpath:application.properties")
@PropertySource(value = "classpath:propiedades.properties")
public class BackRedApplication {

    public static void main(String[] args) {
    SpringApplication.run(BackRedApplication.class, args);
}

    @Bean
    public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
       return new PropertySourcesPlaceholderConfigurer();
    }
}

密码.xml

...
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.0-SNAPSHOT</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.red.api</groupId>
<artifactId>RED</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>RED</name>
<description>RED</description>
<properties>
    <java.version>11</java.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-ldap</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jersey</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-boot-starter</artifactId>
        <version>3.0.0</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>3.0.0</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.json</groupId>
        <artifactId>json</artifactId>
        <version>20200518</version>
    </dependency>
    <dependency>
        <groupId>net.sf.jasperreports</groupId>
        <artifactId>jasperreports</artifactId>
        <version>6.8.0</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>io</artifactId>
        <version>7.1.9</version>
    </dependency>
    
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>kernel</artifactId>
        <version>7.1.9</version>
    </dependency>
    
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>layout</artifactId>
        <version>7.1.9</version>
    </dependency>
</dependencies>
...

配置Class

package com.red.api.configuracion;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.ObjectMapper;

import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;


@Configuration
@EnableSwagger2
public class Configuracion {

    @Bean
    public RestTemplate rest() {
        return new RestTemplate();
    }

    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }

    @Bean
    public Docket redApi() {
        return new Docket(DocumentationType.SWAGGER_2).select()
                .apis(RequestHandlerSelectors.basePackage("com.co.dejsoftware.red.ws"))
                .paths(PathSelectors.any()).build();
    }

}

Controller

package com.red.api.ws;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.red.api.interfaces.InterfaceSeguridad;

@RestController
@CrossOrigin
@RequestMapping("/seguridad")
public class WsSeguridad {

    private Logger logger = LogManager.getLogger(WsSeguridad.class);

    @Autowired
    private InterfaceSeguridad servicioSeguridad;

    @PostMapping(path="/getToken", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Object> getToken(@RequestHeader(name = "user") String usuario, 
            @RequestHeader(name = "pwd") String contrasena) {
    
        try {
        
            return ResponseEntity.ok(servicioSeguridad.getToken(usuario, contrasena));
        
        } catch (Exception e) {
            logger.error(e);
        
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

界面

package com.red.api.interfaces;

import org.springframework.http.ResponseEntity;

public interface InterfaceSeguridad {

    public ResponseEntity<Object> getToken(String usuario, String contrasena);
}

执行

package com.red.api.implementacion;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import com.red.api.interfaces.InterfaceSeguridad;

@Service
public class ServicioSeguridad implements InterfaceSeguridad {

    private Logger logger = LogManager.getLogger(ServicioSeguridad.class);

    public ResponseEntity<Object> getToken(String usuario, String contrasena) {
        try {
            System.out.println("usuario... " + usuario);
            System.out.println("contrasena... " + contrasena);
        
            JSONObject item = new JSONObject();
            item.put("description", "Success...");
            item.put("usuario", usuario);
            item.put("contrasena", contrasena);
            String jsonResponse = new JSONObject().put("exito", item).toString();
        
            return ResponseEntity.ok(jsonResponse);
        }catch (Exception e) {
            logger.error(e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

我知道这并不能直接解决您的问题,但可以考虑转向springdoc Springfox 在这一点上是如此糟糕,使用起来很痛苦。 由于 Spring WebFlux 支持,我在 2 年前搬到了springdoc ,对此我感到非常高兴。 此外,它还支持 Kotlin 协程,我不确定 Springfox 是否支持。

如果您决定迁移, springdoc甚至还有迁移指南

问题似乎是 Spring Boot 从 Ant Path Matching 切换到使用PathPatternParser ,请参见

Springfox 插件有一段时间没有积极维护,因此无法处理与PathPatternParser请求映射。 至少在 3.0.0 版本中。

如果坚持在Spring Boot >= 2.6的情况下继续使用Springfox,可以尝试通过设置强制使用Ant Path Matching

spring.mvc.pathmatch.matching-strategy=ant_path_matcher

强制执行器上的蚂蚁路径匹配是一个单独的问题。 它通过注入在WebMvcEndpointHandlerMapping更改之前自动配置的WebMvcEndpointManagementContextConfiguration

@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 = shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
  return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes,
      corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath),
      shouldRegisterLinksMapping);
}

private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment,
    String basePath) {
  return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath)
      || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}

可能有一种更聪明的方法,首先将执行器排除在 Springfox 分析之外。

你的里程可能会有所不同。 切换到 springdoc 可能是更有价值的方法。

我有一个类似的问题,我试图升级到 Spring 2.6.6。

我建议使用 openapi generator >= 5.4.0 因为它删除了 springfox 作为默认文档提供者

https://github.com/OpenAPITools/openapi-generator/pull/11181修复https://github.com/OpenAPITools/openapi-generator/issues/10409

springdoc 是默认的

有关 spring 引导生成器选项的更多信息,请参阅https://openapi-generator.tech/docs/generators/spring/并搜索 documentationProvider

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM