3

After adding the springdoc-openapi-ui dependency to my Spring project (not Spring Boot) OpenAPI V3 documentation is generated and can be viewed using the default swagger-ui page: localhost:8080/swagger-ui.html. Because the springdoc documentation replaces previous Swagger documentation I want to make it available at the same URL, localhost:8080/docs/index.html. Based on the springdoc documentation I get the impression that can be done by using the springdoc.swagger-ui.path option in the application.properties:

springdoc.swagger-ui.path=/docs/index.html

However, where I would expect to be able to navigate to the API documentation by going to localhost:8080/docs/index.html I get a 404 instead, localhost:8080/swagger-ui.html still works but now redirects to http://localhost:8080/docs/swagger-ui/index.html?configUrl=/restapi/v3/api-docs/swagger-config.

How can I configure my project too make the swagger-ui page available through a custom URL, i.e. localhost:8080/docs/index.html instead of the default localhost:8080/swagger-ui.html?

Edit

After trying some more to get it working and looking through the available information online, such as the springdoc FAQ (mentioned in this answer by H3AR7B3A7) I couldn't get it working. I've decided to go with a different solution which should have the same effect. The springdoc.swagger-ui.path option allows specifying a custom URL but, as I understand it, going to the custom URL redirects a user to the standard localhost:8080/swagger-ui.html page. So the redirect is now configured manually:

@RequestMapping("/docs/index.html")
public void apiDocumentation(HttpServletResponse response) throws IOException {
  response.sendRedirect("/swagger-ui.html");
}
Pieter
  • 115
  • 1
  • 1
  • 11
  • This should work, although using 'localhost:8080/docs/index.html' will redirect you to 'http://localhost:8080/docs/swagger-ui/index.html?configUrl=/restapi/v3/api-docs/swagger-config'. Do you have any other related properties set? Or a config class that you can share? Using 'localhost:8080/swagger-ui.html' with these properties gives me a whitelabel. – H3AR7B3A7 Jul 21 '21 at 14:00
  • Yeah, it was my understanding that if the `springdoc.swagger-ui.path` option worked you would be redirected to `http://localhost:8080/docs/swagger-ui/index.html?configUrl=/restapi/v3/api-docs/swagger-config`. There is a config class that extends `AuthenticationStatelessContextConfiguration`, it overrides the `protected void configure(HttpSecurity http) throws Exception` method and creates a `CorsConfigurationSource corsConfigurationSource()` bean. That seems to be the only config remotely possible to cause the problem but even that seems farfetched. – Pieter Jul 21 '21 at 14:19
  • Hmm, that is weird behavior. Those config shouldn't be the problem, because localhost:8080/swagger-ui.html works. I was thinking of SpringDocConfiguration or SpringDocConfigProperties beans, because it looks like some other config is overwriting your properties, or your properties aren't being picked up for some reason. – H3AR7B3A7 Jul 21 '21 at 14:25
  • If the code is not proprietary or sensitive in any way, I'd love to take a look... But otherwise there's not much more I can do than share my thoughts. XD – H3AR7B3A7 Jul 21 '21 at 14:26
  • Maybe there is a config problem because it is a Spring application instead of a Spring Boot application and we don't properly config springdoc? Based on the springdoc FAQ the `org.springdoc.core.SpringDocConfiguration.class` and `org.springdoc.core.SpringDocConfigProperties.class` are added to the context. The default `localhost:8080/swagger-ui.html` works and the `springdoc.swagger-ui.path` option does something as the values configured show up in the URL to which you get redirected, although in a different form than I would expect. – Pieter Jul 21 '21 at 14:40
  • We do have a URL base/prefix configured, could that be causing a conflict somehow? The name of the application gets added to the URL so instead of going to `localhost:8080/swagger-ui.html` you actually need to go to `localhost:8080/appname/swagger-ui.html`. – Pieter Jul 23 '21 at 08:53
  • No, in my example I have set 'server.servlet.context-path: /appName'. Swagger should then just be available at 'localhost:8080/appname/index.html' and no longer at 'localhost:8080/appname/swagger-ui.html''. I still feel like your swagger-ui.path is not being picked up for some reason... All I can tell you for sure, is that it works for my project. XD – H3AR7B3A7 Jul 23 '21 at 16:08

3 Answers3

2

I had a similar task for Spring Boot 2.5.6 and springdoc-openapi-webflux-ui 1.5.12. I've found several possible solutions for myself. Maybe it will be helpful for somebody else.


Set springdoc.swagger-ui.path directly

The straightforward way is to set property springdoc.swagger-ui.path=/custom/path. It will work perfectly if you can hardcode swagger path in your application.


Override springdoc.swagger-ui.path property

You can change default swagger-ui path programmatically using ApplicationListener<ApplicationPreparedEvent>. The idea is simple - override springdoc.swagger-ui.path=/custom/path before your Spring Boot application starts.

@Component
public class SwaggerConfiguration implements ApplicationListener<ApplicationPreparedEvent> {

    @Override
    public void onApplicationEvent(final ApplicationPreparedEvent event) {
        ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();
        Properties props = new Properties();
        props.put("springdoc.swagger-ui.path", swaggerPath());
        environment.getPropertySources()
                .addFirst(new PropertiesPropertySource("programmatically", props));
    }

    private String swaggerPath() {
        return "/swagger/path"; //todo: implement your logic here.
    }
}

In this case, you must register the listener before your application start:

@SpringBootApplication
@OpenAPIDefinition(info = @Info(title = "APIs", version = "0.0.1", description = "APIs v0.0.1"))
public class App {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(App.class);
        application.addListeners(new SwaggerConfiguration());
        application.run(args);
    }
}

Redirect using controller


You can also register your own controller and make a simple redirect (the same as what you suggest, but in my case, I need to use the WebFlux approach):

@RestController
public class SwaggerEndpoint {

    @GetMapping("/custom/path")
    public Mono<Void> api(ServerHttpResponse response) {
        response.setStatusCode(HttpStatus.PERMANENT_REDIRECT);
        response.getHeaders().setLocation(URI.create("/swagger-ui.html"));
        return response.setComplete();
    }
}

The problem with such an approach - your server will still respond if you call it by address "/swagger-ui.html".

Volodya Lombrozo
  • 2,325
  • 2
  • 16
  • 34
1

Apparantly the library integrates natively only with spring-boot applications like you mentioned in your comment. If you want to use spring, it's possible but the integration details aren't documented, because it really depends on the version/module and the nature of you spring application.

You can check the FAQ to see if it answers your questions.

There are some more answers here on SO.

H3AR7B3A7
  • 4,366
  • 2
  • 14
  • 37
0

I solved by a clear code. The problem is in webflux that security need permitAll for some uri resources so I solved in this mode in WebFlux Security I image the same in Springboot no WebFlux:

                .authorizeExchange().pathMatchers(
// START To show swagger 3:
                        "/swagger-ui.html",
                        "/webjars/swagger-ui/**",
                        "/v3/api-docs/swagger-config",
                        "/v3/api-docs", // This is the one URI resource used by openApi3 too.
// END To show swagger 3:
                        "/other-uri-permitAll",
                        ...
                        "/other-uri-permitAll_N",
                   .permitAll()
                .and()
                .authorizeExchange().pathMatchers(
                        "/other-uri-authenticated-only_1",
                        "...",
                        "/other-uri-authenticated-only_1")
                    .authenticated()
                .and()
                .authenticationManager(myAuthenticationManager)
                .securityContextRepository(mySecurityContextRepository)
                .authorizeExchange().anyExchange().authenticated()
                .and()
                .build();
SkyBlackHawk
  • 95
  • 10