Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customizing swagger-ui.html location #1080

Closed
rockytriton opened this issue Dec 2, 2015 · 41 comments
Closed

Customizing swagger-ui.html location #1080

rockytriton opened this issue Dec 2, 2015 · 41 comments

Comments

@rockytriton
Copy link

@rockytriton rockytriton commented Dec 2, 2015

I've seen that we can customize the swagger api-docs location, i would also like to customize the location of swagger-ui.html and /configuration/ui, /configuration/security. I can't find anywhere to do this, is it possible?

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Dec 3, 2015

/configuration/ui and /configuration/security can't be changed unfortunately. Those are known locations to get this information. You could certainly change swagger-ui.html, if you're using spring-boot thats the default behavior, and you override that using an implementation of WebMvcConfigurerAdapter that adds your endpoint using the addResourceHandlers

@dilipkrish dilipkrish added this to the 2.3.0 milestone Dec 3, 2015
@dilipkrish dilipkrish closed this Dec 5, 2015
@chornyi
Copy link
Contributor

@chornyi chornyi commented Jan 6, 2016

I was able to move Swagger UI under /documentation using this code.

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry.addRedirectViewController("/documentation/v2/api-docs", "/v2/api-docs");
    registry.addRedirectViewController("/documentation/configuration/ui", "/configuration/ui");
    registry.addRedirectViewController("/documentation/configuration/security", "/configuration/security");
    registry.addRedirectViewController("/documentation/swagger-resources", "/swagger-resources");
    registry.addRedirectViewController("/documentation", "/documentation/swagger-ui.html");
    registry.addRedirectViewController("/documentation/", "/documentation/swagger-ui.html");
}

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

However it still requires a redirect to /documentation/swagger-ui.html because the path name is hard-coded here: https://github.com/springfox/springfox/blob/master/springfox-swagger-ui/src/web/js/springfox.js#L4. Maybe this is something that could be improved.

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Jan 6, 2016

@chornyi 👍 thanks for the work-around... will chalk that up as a documentation enhancement

@nedkoh
Copy link

@nedkoh nedkoh commented Feb 8, 2016

Is this issue resolved? I want to be able to change to /api/ and will need this fix to make it work.

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Feb 8, 2016

@nedkoh there is nothing to resolve. Following @chornyi's instructions should help you. Just replace documentation with api

@nedkoh
Copy link

@nedkoh nedkoh commented Feb 8, 2016

@dilipkrish I was referring to this part but did not realize the redirect is working as a workaround. Thanks. "However it still requires a redirect to /documentation/swagger-ui.html because the path name is hard-coded here: https://github.com/springfox/springfox/blob/master/springfox-swagger-ui/src/web/js/springfox.js#L4"

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Feb 8, 2016

@nedkoh oh I see. Sorry for the confusion! As a library that is pretty hard (and out of scope) to make configurable, which is why it is just tagged as a can-use-for-docs and will be documented as when you want to change this behavior, what should one do?

If you do see a way to make that easier would love feedback and a PR would be even better 😉

@nedkoh
Copy link

@nedkoh nedkoh commented Feb 8, 2016

I had gotten it to work without redirect using the other swagger github project but it required some ugly .js manipulation of the static content and a JSP page which is not ideal. Will think of a cleaner solution. btw. I just tried the above and everything worked as expected.

@jeroenbellen
Copy link

@jeroenbellen jeroenbellen commented Feb 19, 2016

Why is this issue closed? Redirecting is not a solution?
It should be possible to configure all endpoints to listen to a provided path.

@jeanchang22
Copy link

@jeanchang22 jeanchang22 commented Jun 22, 2016

This fix doesn't work for groups, could you help to find a better solution for it?

@jeanchang22
Copy link

@jeanchang22 jeanchang22 commented Jun 22, 2016

my projects have multiple versions of documents, on swagger-ui.html, there is a dropdown to link to different version of doc, however, after applying this fix, it always redirects to the first version of doc, the link to other versions of doc are no longer valid.

@gstaykov
Copy link

@gstaykov gstaykov commented Apr 18, 2017

For version 2.6.1 if you want your groups to work too use this:

@Override
public void addViewControllers(ViewControllerRegistry registry) {
	registry.addRedirectViewController("/documentation/v2/api-docs", "/v2/api-docs").setKeepQueryParams(true);
	registry.addRedirectViewController("/documentation/swagger-resources/configuration/ui","/swagger-resources/configuration/ui");
	registry.addRedirectViewController("/documentation/swagger-resources/configuration/security","/swagger-resources/configuration/security");
	registry.addRedirectViewController("/documentation/swagger-resources", "/swagger-resources");
}

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

After that you can access the swagger-ui here: http://localhost:8080/documentation/swagger-ui.html

In this way the default group and the custom groups will work because of setKeepQueryParams(true) on the redirect. If you have custom path for the /v2/api-docs use it in the redirect ! For example if you have springfox.documentation.swagger.v2.path: /private/v2/api-docs for custom api-docs path your

registry.addRedirectViewController("/documentation/v2/api-docs", "/v2/api-docs").setKeepQueryParams(true);

should look like this:

registry.addRedirectViewController("/documentation/private/v2/api-docs", "/private/v2/api-docs").setKeepQueryParams(true);

This is still workaround. Enjoy

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Apr 18, 2017

Thanks @georgi-staykov for the useful tip!!

@sankarav
Copy link

@sankarav sankarav commented Jul 20, 2017

Thanks @chornyi for comments, it worked. But I am using spring websecurity in my application. In this case I had to do the following, so the requests related to swagger redirects get to the application.


@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

     @Override
      protected void configure(HttpSecurity http) throws Exception {
         http.authorizeRequests()
            .antMatchers(HttpMethod.GET, "/v2/api-docs").permitAll()
            .antMatchers(HttpMethod.GET,"/swagger-resources").permitAll()
            .antMatchers(HttpMethod.GET,"/configuration/**").permitAll()
            .antMatchers(HttpMethod.GET,"/documentation/**").permitAll()
            .antMatchers("/**").fullyAuthenticated()
            .and().csrf().disable();
      }
}

Is there a way I could modify your spring redirects/forwards to keep my security authorizations to minimum? I am expecting something like this, so everything under "/documentation/**" is authenticated for swagger-ui.


@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

     @Override
      protected void configure(HttpSecurity http) throws Exception {
         http.authorizeRequests()
            .antMatchers(HttpMethod.GET,"/documentation/**").permitAll()
            .antMatchers("/**").fullyAuthenticated()
            .and().csrf().disable();
      }
}

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Jul 21, 2017

I think you can get away with
.antMatchers(HttpMethod.GET,"/swagger-resources/**").permitAll()

@KD4
Copy link

@KD4 KD4 commented Dec 6, 2017

Is there no solution without redirecting? T_T

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Dec 6, 2017

@KD4 Solution to what?

@river226
Copy link

@river226 river226 commented Mar 28, 2018

@dilipkrish The issue is that the only way to customize the url is a redirect. It would be nice if springfox had some built in way to make the necessary updates when it builds the swagger doc. Something like this:https://github.com/domaindrivendev/Swashbuckle#customizing-the-generated-swagger-docs without a need to actually redirect which is at best makeup on the pig that is this problem.

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Mar 28, 2018

@river226 You can customize the swagger description url. Its only a problem if you want your swagger-ui to be in a different place than the root of the context path.

The location of swagger-ui.html, the built in one, is like the north star for where all the other endpoints are located, ui configuration, security configuration, the different grouped swagger descriptions etc.

Im not familiar with swashbuckle, but what specifically of the customizations in swashbuckle are you expecting to see here? Could you perhaps provide an example of a proposed configuration? That will give me a better idea of the problem you're trying to solve with the makeup :)

@river226
Copy link

@river226 river226 commented Mar 29, 2018

@dilipkrish Upon a second look, it looks like swashbuckle may not do what I was aiming for. What I and I am pretty sure most of the people in this thread are looking for is simply changing the north star. Instead of swagger-ui.html and make it say: "thisisuseful.whyisntthisathing". I find the idea as you posted previously that this is hard to make configurable doubtful at best, and setting up a workaround redirect is not a good solution to an obviously useful feature.

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Mar 29, 2018

@river226 Open to ideas

@bob9
Copy link

@bob9 bob9 commented Mar 31, 2018

I solved this problem by using rewrite rule..
add to dependancy

compile group: 'org.tuckey', name: 'urlrewritefilter', version: '4.0.4'

Make sure this component is in your @componentscan path

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import org.tuckey.web.filters.urlrewrite.Conf;
import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter;

import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import java.io.IOException;


@Component
public class MyUrlRewriteFilter extends UrlRewriteFilter {

    private static final String CONFIG_LOCATION = "classpath:/urlrewrite.xml";

    //Inject the Resource from the given location
    @Value(CONFIG_LOCATION)
    private Resource resource;

    //Override the loadUrlRewriter method, and write your own implementation
    @Override
    protected void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
        try {
            //Create a UrlRewrite Conf object with the injected resource
            Conf conf = new Conf(filterConfig.getServletContext(), resource.getInputStream(), resource.getFilename(), "@@yourOwnSystemId@@");
            checkConf(conf);
        } catch (IOException ex) {
            throw new ServletException("Unable to load URL rewrite configuration file from " + CONFIG_LOCATION, ex);
        }
    }
}

Rewrite rules xml put this in resources files name the file urlrewrite.xml.
Change /users/ to be whatever folder to want it to the rewrite rule to to navigate to.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN"
        "http://www.tuckey.org/res/dtds/urlrewrite4.0.dtd">
<urlrewrite>
    <rule>
        <from>/users/swagger-ui.html</from>
        <to type="passthrough">/swagger-ui.html</to>
    </rule>

    <rule>
        <from>/users/webjars/(.*)</from>
        <to type="passthrough">/webjars/$1</to>
    </rule>

    <rule>
        <from>/users/api-docs</from>
        <to type="passthrough">/api-docs</to>
    </rule>

    <rule>
    <from>/users/configuration/(.*)</from>
    <to type="passthrough">/configuration/$1</to>
    </rule>

    <rule>
    <from>/users/swagger-resources</from>
    <to type="passthrough">/swagger-resources</to>
</rule>
</urlrewrite>
@alfredyctan
Copy link

@alfredyctan alfredyctan commented Apr 4, 2018

The simplest way I found is extends some of the swagger builtin controller with new @RequestMapping
code: https://github.com/alfredyctan/afc/tree/master/afc-util/src/main/java/org/afc/swagger/docs, https://github.com/alfredyctan/example/tree/master/swagger/petstore-service

This allow parallel run with default and custom path swagger ui, at the end I can have this url running
base context - http://localhost:8080/petstore
actuator - http://localhost:8080/petstore/actuator
default swagger ui - http://localhost:8080/petstore/swagger-ui.html
custom swagger ui - http://localhost:8080/petstore/api/v2/docs/swagger-ui.html
API - http://localhost:8080/petstore/api/v2/user/login

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Apr 4, 2018

@alfredyctan and @bob9 those are both interesting approaches that seem to work. Thanks for sharing!

@river226
Copy link

@river226 river226 commented Apr 5, 2018

@dilipkrish it would be awesome if one of these approaches were fully supported by the library and even better if they could be used with 1 or 2 additional lines of code.

@nahguam
Copy link

@nahguam nahguam commented Apr 16, 2018

Hi @dilipkrish ,

This is a feature that would also be useful for us. We have multiple apps, each with Swagger endpoints being exposed through Kubernetes Services and an AWS ALB Ingress. Each has to be on a separate endpoint. Up until now we've used the Spring Boot contextPath but due to another requirement we can no longer do that so we need another way to relocate the docs. Redirects will not cut it.

I've spent a little time having a look and have got it working by introducing a Swagger contextPath, that sits after the Spring Boot contextPath. I've started turning it into PR but wanted to get your input on the idea first.

master...nahguam:contextPath

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Apr 17, 2018

@nahguam Im going to preface by saying, Im not sure a generic solution that can be applied to this particular problem.

The swagger-ui that is bolted on to this library is only a convenience for the 80% of the use cases. For the remaining 20%, it may be better to not generalize and over-engineer a solution. Not that yours is, Im just expressing the philosophy behind the swagger-ui that is bundled in springfox.

Having said that I like you're idea. We could perhaps build on it so here is my feedback"

  • swagger-ui that is bundled is adheres to the web jar specification, so the default behavior is to register resource handlers automatically for spring boot apps.
  • EnableWebMvc is a total no-no when it comes to spring boot apps. It is a signal to tell spring boot you're going to manage your own configuration. A library like springfox shouldnt make that determination.
  • We just put considerable effort in removing dependence on RequestMappings with configuration expressions. This is so that we dont assume there is a PropertySourcesPlaceholderConfigurer

There is already a solution to let you aggregate swagger specifications from multiple services. Its used in projects like jhipster. That might be a thing you could consider.

@nahguam
Copy link

@nahguam nahguam commented Apr 17, 2018

Hi @dilipkrish,

This is great feedback, Thanks.

I haven't considered how to test this yet. Any pointers here?

Edit: I've had a bit more of a dig:

  • Re RequestMapping, I see you have PropertySourcedMapping which is used by PropertySourcedRequestMappingHandlerMapping. However this only scans methods, not types, so is unable to construct a new path if the mapping is on both the type and method, such as with ApiResourceController.
  • I see your point regarding EnableWebMvc and overriding the WebMvcConfigurer but I don't see another way to remap the webjar. I appreciate it would affect all webjars on the classpath. The intent there was that EnableWebMvc would only be enabled if a context path has been specified.

Just taking a step back, is it possible to aggregate swagger specs hosted on other servers? Is that what you meant by 'multiple services'?

@dilipkrish
Copy link
Member

@dilipkrish dilipkrish commented Apr 19, 2018

@nahguam as far as pointers on how to test, sadly it appears I punted testing PropertySourcedRequestMappingHandlerMapping :(

However this only scans methods, not types, so is unable to construct a new path if the mapping is on both the type and method, such as with ApiResourceController.

That is true. It was targeting a very simple use case of replacing the path without using PropertySourcesPlaceholderConfigurer

Just taking a step back, is it possible to aggregate swagger specs hosted on other servers? Is that what you meant by 'multiple services'?

It is totally possible. That is what I meant to say, sorry I wasnt clear. Yes, to clarify, you could have one endpoint that hosts swagger-ui (the springfox one) and it can aggregate services living in-the same app and/or in files or combine external swagger specifications in other services. You just need to implement your custom SwaggerResourcesProvider.

@zpf7879
Copy link

@zpf7879 zpf7879 commented Sep 10, 2018

My app is a Spring WebFlux app, and I tried all the approaches listed here in the thread. Unfortunately, none of them works in this context. I end up the following solution by using WebFilter (I personally feel not very gracefully but no better approach found yet).

@Component
public class SwaggerWebFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().pathWithinApplication().value();
        if (path.startsWith("/api/swagger-ui.html") || path.startsWith("/api/webjars")
                || path.startsWith("/api/api-docs") || path.startsWith("/api/configuration")
                || path.startsWith("/api/swagger-resources") || path.startsWith("/api/v2")) {
            exchange = exchange.mutate().request(request.mutate().path(path.substring(4)).build()).build();
        }
        return chain.filter(exchange);
    }
}
@n2696118
Copy link

@n2696118 n2696118 commented Dec 7, 2018

It seems the only way to fix it (without resorting to redirects) to fork the repository and start renaming... Which resources would need to be changed for everything to work?

@x1aliang
Copy link

@x1aliang x1aliang commented Apr 10, 2019

@Configuration
@EnableSwagger2
@Controller
public class SwaggerConfig implements WebMvcConfigurer{
	@Bean
    public Docket swaggerSpringMvcPlugin() {
        return new Docket(DocumentationType.SWAGGER_2)
        		.select()
        		.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
        		.build()
        		.securitySchemes(Collections.singletonList(new ApiKey("Bearer", "Authorization", "header")));
    }

	
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/api-docs/**").addResourceLocations("classpath:/META-INF/resources/");
	}
	
	@RequestMapping("/api-docs/swagger-resources")
	public String resource() {
		return "forward:/swagger-resources";
	}
	
	@RequestMapping("/api-docs/swagger-resources/configuration/ui")
	public String ui() {
		return "forward:/swagger-resources/configuration/ui";
	}
	
	@RequestMapping("/api-docs/v2/api-docs")
	public String doc() {
		return "forward:/v2/api-docs";
	}
	

	@RequestMapping("/api-docs/swagger-resources/configuration/security")
	public String security() {
		return "forward:/swagger-resources/configuration/security";
	}
}

Although this issue has been closed for a while, but i think we can use this way to redirect the swagger ui since which dnt need to modify spring security settings.

@sarangajay
Copy link

@sarangajay sarangajay commented Apr 10, 2019

Hi x1aliang

I have an issue that unless i have the below (/**) , swagger-ui.html does not get load. and doing so impact other handles as now other servlets can't fine the appropriate handlers.

registry.
addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/");

any idea ?

this a non spring boot + xml app.

@Numbernick
Copy link

@Numbernick Numbernick commented Apr 23, 2019

Wow, this conversation got huuuge...

In my honest opinion the base path of the swagger-ui should be a configuration (I mean e.g. even the spring actuator can have a different base path). I would recomment to add the "SwaggerWebFilter" written from @zpf7879 to the swagger-ui project. I made some small changes to the Filter to support the Spring PathMatcher to have a spring way behaviour in writing the urls.

@Service
class InternalRequestRewriteFilter(
        @Value("\${server.servlet.context-path}") private val contextPath: String): WebFilter {
    private val pathMatcher = AntPathMatcher()
    private val swaggerPaths = setOf("swagger-ui.html", "webjars/springfox-*/**", "swagger-resources/**", "v2/api-docs")
    private val logger = getLogger(javaClass)

    override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
        // Get the path of the request
        val path = exchange.request.path.pathWithinApplication().value()
        // If the path leads to an swagger element -> remove the context path for the internal processing
        val modifiedExchange = if (isSwaggerRequest(exchange.request)) {
            logger.debug("""Swagger request detected for path "$path"""")
            exchange.mutate().request(exchange.request.mutate().path(path.replaceFirst(contextPath, "")).build()).build()
        } else {
            exchange
        }
        // Execute the normal filter chain
        return chain.filter(modifiedExchange)
    }

    private fun isSwaggerRequest(request: ServerHttpRequest): Boolean {
        // Get the path of the request
        val path = request.path.pathWithinApplication().value()

        return swaggerPaths.any {swaggerPath -> pathMatcher.match("$contextPath/$swaggerPath", path)}
    }
}

So even with the context-path of "" (empty String) this works like a charm (except for csrf, which I just didn't configure. But with the referer in place it should work as well).

@dilipkrish Please add a basic behaviour to springfox to close this 3 year old topic with so many different and awesome workarrounds.

@publisherfk
Copy link

@publisherfk publisherfk commented Feb 21, 2020

in the 2.9.2,some path had changed,eg:

registry.addRedirectViewController("/api/swagger-resources/configuration/ui", "/swagger-resources/configuration/ui");
registry.addRedirectViewController("/api/swagger-resources/configuration/security", "/swagger-resources/configuration/security");
@Korzi
Copy link

@Korzi Korzi commented Oct 29, 2020

Apart from property springfox.documentation.swagger.v2.path (mentioned in the doc ) that allows to modify default /v2/api-docs path,
you can use property springfox.documentation.swagger-ui.base-urlto change default /swagger-ui/ to custom path

@kanai0618
Copy link

@kanai0618 kanai0618 commented Jan 18, 2021

Hi @zpf7879 @dilipkrish , i am using spring webflux, my swagger url doesnot work with this url : http://localhost:8080/swagger-ui/index.html but works with http://localhost:8080/webjars/swagger-ui/index.html, why webjars needed in the URL ? how to fix that ?

@publisherfk
Copy link

@publisherfk publisherfk commented Jan 18, 2021

@kanai0618 I guest your project set project path is webjars,Please check it.

@kanai0618
Copy link

@kanai0618 kanai0618 commented Jan 18, 2021

Hi @publisherfk , i have not set any project path, is there any way to find out ? or by default its set to webjars ?

@publisherfk
Copy link

@publisherfk publisherfk commented Jan 19, 2021

Hi @kanai0618 maybe project web.xml had set path ,maybe webjar project have default path.

@pdAfy51
Copy link

@pdAfy51 pdAfy51 commented Apr 9, 2021

Hi this solution works for me https://stackoverflow.com/a/59948001

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet