Alessandro C
Alessandro C

Reputation: 3560

Springfox swagger 2 not working with Spring Boot 1.5: HTTP 404 not found at /v2/api-docs

I have a Spring Boot project with springfox-swagger-2 as dependency.

Versions used:

This is the configuration:

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket api() {
        Docket api = new Docket(DocumentationType.SWAGGER_2);
        api
            .select()
            .apis(RequestHandlerSelectors.any())
            .paths(PathSelectors.any())
            .build();

            api.apiInfo(apiInfo())
                .globalOperationParameters(Lists.newArrayList(new ParameterBuilder()
                .name("Example api info")
                .description("description")
                .modelRef(new ModelRef("string"))
                .parameterType("parameter type example").build()))
            ;
        return api;
    }

    @SuppressWarnings("rawtypes")
    private ApiInfo apiInfo() {
        Contact contact = new Contact("name", "url", "email");
        Collection<VendorExtension> vendorExtensions = new ArrayList<>();
        return new ApiInfo("title", "description", "version", "termsOfServiceUrl", contact, "license", "licenseUrl", vendorExtensions);
    }
}

The application starts correctly, but the url /v2/api-docs gets an HTTP 404 Not Found

Even the /swagger-ui.html is not available adding the dependency for springfox-swagger-ui

The bootstrap log doesn't report any error.

I already tried to find the answer on other similar questions but any of them is working!

Any help would be appreciated.

Upvotes: 0

Views: 6308

Answers (5)

Asma Rahim Ali Jafri
Asma Rahim Ali Jafri

Reputation: 1383

In case the person stuck is a noob like me, make sure you have run the Maven Install command after adding the dependencies in the pom.xml file.

Upvotes: 0

HeatZync
HeatZync

Reputation: 330

I also stumbled upon an HTTP 404 Not Found for /v2/api-docs (but during a unit test) as part of migrating Spring Boot from version 2.0.4.RELEASE to version 2.1.6.RELEASE. The unit test passed before the "upgrade".

The unit test class had the following annotations:

@Category(UnitIntegrationTest.class)
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(classes = {SecurityConfiguration.class})
@ActiveProfiles("test")

and the test configuration was defined as an inner class:

@Configuration
@EnableWebMvc
@EnableSwagger2
@Import(value = BeanValidatorPluginsConfiguration.class)
public static class TestSwaggerConfiguration {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.basePackage("the.package.we.want"))
            .paths(PathSelectors.any())
            .build();
    }
}

The fix was to specify the TestSwaggerConfiguration in the @ContextConfiguration, e.g.:

@Category(UnitIntegrationTest.class)
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(classes = {SecurityConfiguration.class, GenerateDocumentationTest.TestSwaggerConfiguration.class})
@ActiveProfiles("test")

As a side note, before hitting the HTTP 404 I also had to specify

spring.main.allow-bean-definition-overriding=true

in the application-test.properties as per the Spring Boot 2.1 Release Notes.

Upvotes: 0

Alessandro C
Alessandro C

Reputation: 3560

Finally I have found the way to make it work.

The springfox-swagger-2 implementation has a @Controller within the class springfox.documentation.swagger2.web.Swagger2Controller.

This class implements the mapping for the url "/v2/api-docs" with this method:

@RequestMapping(
    value = DEFAULT_URL,
    method = RequestMethod.GET,
    produces = { APPLICATION_JSON_VALUE, HAL_MEDIA_TYPE })
@PropertySourcedMapping(
    value = "${springfox.documentation.swagger.v2.path}",
    propertyKey = "springfox.documentation.swagger.v2.path")
@ResponseBody
public ResponseEntity<Json> getDocumentation(
    @RequestParam(value = "group", required = false) String swaggerGroup,
    HttpServletRequest servletRequest) {

  String groupName = Optional.fromNullable(swaggerGroup).or(Docket.DEFAULT_GROUP_NAME);
  Documentation documentation = documentationCache.documentationByGroup(groupName);
  if (documentation == null) {
    return new ResponseEntity<Json>(HttpStatus.NOT_FOUND);
  }
  Swagger swagger = mapper.mapDocumentation(documentation);
  UriComponents uriComponents = componentsFrom(servletRequest, swagger.getBasePath());
  swagger.basePath(Strings.isNullOrEmpty(uriComponents.getPath()) ? "/" : uriComponents.getPath());
  if (isNullOrEmpty(swagger.getHost())) {
    swagger.host(hostName(uriComponents));
  }
  return new ResponseEntity<Json>(jsonSerializer.toJson(swagger), HttpStatus.OK);
}

As you can see, the RequestMapping expects a parameter named "group". So, if you call the "/v2/api-docs" url without the "group" parameter, the documentation obtained is null because there are no documentations in the cache for the key "" (empty String).

I solved adding a custom Filter implemented in this way:

@Component
public class SwaggerFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        String group = req.getParameter("group");

        if (req.getServletPath().equals("/v2/api-docs") && group==null) {
            res.sendRedirect("api-docs?group=default");
        } else {
            chain.doFilter(request, response);
        }

    }

    @Override
    public void destroy() {

    }

}

The mechanism is simple: without the "group" parameter, there is a redirect with the "default" group parameter.

Upvotes: 0

Romil Patel
Romil Patel

Reputation: 13727

SwaggerConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 SwaggerConfig {
   @Bean
   public Docket apiDocket() {

       Docket docket =  new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.."))
                .paths(PathSelectors.any())
                .build();

       return docket;

    } 
}

SecurityConfig.java

public class SecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer{

    @Override
        public void configure(WebSecurity web) throws Exception {
            web
              .ignoring()
                .antMatchers("/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**")
        }

    @Override
        protected void configure(HttpSecurity http) throws Exception{

             http
             .csrf().disable()
             .authorizeRequests()
             .antMatchers("/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**")
             .permitAll()
             .anyRequest().authenticated();
        }

    @Override 
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        }
}

pom.xml

 <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-core</artifactId>
        <version>2.9.2</version>
    </dependency>

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>

Upvotes: 1

Dzmitry Krivolap
Dzmitry Krivolap

Reputation: 1424

try to add this to yourapplication.properties

spring.resources.add-mappings=true

Upvotes: 0

Related Questions