Swagger and Spring MVC (non-spring-boot)

606 views Asked by At

I've been trying to add Springfox-Swagger UI 3.0.0 into a regular Spring MVC application with Spring Security (non-spring-boot) and cannot figure it out. I recently incorporated swagger-ui into 2 spring-boot projects which were much easier without any issue.

I've followed numerous tutorials for this and none of them are working.

The latest tutorial I followed is the official one by SpringFox I also followed the demo projects from https://github.com/springfox/springfox-demos and more specifically the spring-xml-swagger demo

Requests for /swagger-ui/,/swagger-ui/index.html, /swagger-ui.html and /v2/api-docs/ return 404.

I've also tried with older versions of swagger-ui but none of them worked. Since it's a company project I can't reveal too much but any suggestions would be nice. I'll try to update this post with some configuration code if necessary.

Dependencies from pom.xml

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>bootstrap</artifactId>
    <version>3.4.0</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>3.0.0</version>
</dependency>

Spring Framework is 5.2.x

My SwaggerConfig.class.

@EnableSwagger2
@EnableWebMvc
public class SwaggerConfig {
        @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                .apiInfo(apiInfo());
    }
        private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Api Services")
                .description("Api Services")
                .version("v1")
                .build();
    }
    
    @Bean
    public UiConfiguration uiConfiguration() {
        return UiConfigurationBuilder
                .builder()
                .defaultModelsExpandDepth(-1)
                .build();
    }
}

From the xml config.

    <mvc:resources mapping="/swagger-ui/**" location="classpath:/META-INF/resources/webjars/springfox-swagger-ui/"/>
    <mvc:view-controller path="/swagger-ui/" view-name="forward:/swagger-ui/index.html"/>
    <mvc:resources mapping="index.html" location="classpath:/META-INF/resources/webjars/springfox-swagger-ui/"/>
    <bean id="swaggerConfig" class="com.example.config.SwaggerConfig"/>
2

There are 2 answers

0
Stratos On BEST ANSWER

Solved it.

For anyone interested, it turns out that I needed to configure Servlet configuration through web.xml since our current servlet was configured with <url-pattern>*.do</url-pattern> and that was blocking me from accessing the swagger related URLs.

The configuration which solved my issue is the following.

web.xml

<servlet>
        <servlet-name>swagger-ui</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:/swagger-servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>swagger-ui</servlet-name>
        <url-pattern>/swagger-ui/index.html</url-pattern>
        <url-pattern>/swagger-ui/springfox.css</url-pattern>
        <url-pattern>/swagger-ui/swagger-ui.css</url-pattern>
        <url-pattern>/swagger-ui/springfox.js</url-pattern>
        <url-pattern>/swagger-ui/swagger-ui.js</url-pattern>
        <url-pattern>/swagger-ui/swagger-ui-bundle.js</url-pattern>
        <url-pattern>/swagger-ui/swagger-ui-standalone-preset.js</url-pattern>
        <url-pattern>/swagger-ui/favicon-32x32.png</url-pattern>
        <url-pattern>/swagger-ui/favicon-16x16.png</url-pattern>
        <url-pattern>/swagger-resources/configuration/ui</url-pattern>
        <url-pattern>/swagger-resources/configuration/security</url-pattern>
        <url-pattern>/swagger-resources</url-pattern>
        <url-pattern>/v2/api-docs</url-pattern>
    </servlet-mapping>

swagger-servlet-context.xml

    <context:component-scan base-package="my.app.path.config, my.app.path.web.controllers" />
    
    <!-- cache resources to the browser-->
    <mvc:resources mapping="/swagger-ui/**" location="classpath:/META-INF/resources/webjars/springfox-swagger-ui/"/>

    <mvc:interceptors>
        <mvc:interceptor>
        <mvc:mapping path="/swagger-ui/**" />
        <mvc:mapping path="/v2/api-docs/**" />
        </mvc:interceptor>
    </mvc:interceptors>
    
    <!-- Configures the @Controller programming model -->
    <mvc:annotation-driven/>

3
dbreaux On

Ok, I'm not saying this with certainty, but in my experience at least some aspects of Spring MVC seem to be unhappy with a mix of XML and Annotation-based configuration. In particular, @EnableWebMvc.

In any case, our working app, with Spring 5.3.x and SpringFox 3.0, we did some additional things in our @EnableSwagger2 class, including what is specified here:

http://springfox.github.io/springfox/docs/current/#changes-in-swagger-ui

For non-boot applications springfox-swagger-ui is no longer automatically enabled by adding the dependency. It needs to be explicitly register using a resource handler configurer (WebFluxConfigurer or WebMvcConfigurer).

With this example copied from that page, which must be where I got it earlier:

public class SwaggerUiWebMvcConfigurer implements WebMvcConfigurer {
  private final String baseUrl;

  public SwaggerUiWebMvcConfigurer(String baseUrl) {
    this.baseUrl = baseUrl;
  }

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    String baseUrl = StringUtils.trimTrailingCharacter(this.baseUrl, '/');
    registry.
        addResourceHandler(baseUrl + "/swagger-ui/**")
        .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
        .resourceChain(false);
  }

  @Override
  public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController(baseUrl + "/swagger-ui/")
        .setViewName("forward:" + baseUrl + "/swagger-ui/index.html");
  }
}

So, while your XML configuration would seem to be duplicating that, maybe that has to be in Annotation configuration instead, if using @EnableWebMvc.

And/or, it might be that in order to use @Bean in your class, you also have to have @Configuration on the class as well. That's another difference in our setup.

My Spring and SpringFox pom.xml excerpts:

...
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>

        <spring.version>5.3.30</spring.version>
        <springfox.version>3.0.0</springfox.version>
    </properties>

    <dependencies>
...
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${springfox.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${springfox.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-bean-validators</artifactId>
            <version>${springfox.version}</version>
        </dependency>

    </dependencies>
...

Our config class has these annotations:

@Configuration
@EnableWebMvc
@EnableSwagger2
@Import(springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration.class)
public class SpringFoxConfig implements WebMvcConfigurer {

(The last one is specifically for JSR-303 validation awareness; it should not be required for basic operation.)

And, finally, I think the only relevant part of our single Spring XML configuration file, which is loaded from our web.xml file via DispatcherServlet:

    <context:annotation-config/>
    <bean class="our.app.package.api.swagger.SpringFoxConfig"/>

Other potentially-relevant differences from your example:

  • we don't have any custom UiConfiguration bean
  • we use a custom/limited path selector in our Docket builder: .apis(RequestHandlerSelectors.basePackage("our.app.package"))

(I'd be surprised if the latter made a difference.)

Finally, at application start, do you see an entry like this in your server log? (If debug logging is enabled.)

12:17:51,960 DEBUG _org.springframework.web.servlet.HandlerMapping.Mappings:'resourceHandlerMapping' {/swagger-ui/**=ResourceHttpRequestHandler [classpath [META-INF/resources/webjars/springfox-swagger-ui/]]}

Sorry I can't give a more expert, certain answer, but maybe this at least helps. I can at least confirm non-boot SpringMVC with SpringFox can/does work.