question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

3.0.0 Not possible to change base path with NGINX X-Forwarded-Prefix

See original GitHub issue

I just upgraded to the latest version of springfox-swagger2 (3.0.0). We have a proxy server and our X-Forward-Prefix (/headers) are configured (NGINX).

proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Prefix /api/animals;
proxy_set_header X-Real-IP $remote_addr;

With the old version of springfox-swagger2 (2.10.5) we configured our Docket:

return new Docket(DocumentationType.SWAGGER_2) //
    // Workaround for bug https://github.com/springfox/springfox/issues/2622
    .pathProvider(new RelativePathProvider(null) {
    @Override
      public String getApplicationBasePath() {
            return "/api/animals";
       }
    });

This resulted in a base path: [ Base URL: xxxx.com/api/animals/rest ] The /rest part is always present for our rest endpoints and is not part of the reverse proxy.

Swagger ui itself works fine and runs on the https://xxxx.com/api/animals/rest/swagger-ui/index.html

Now according to https://github.com/springfox/springfox/issues/2622 we should not add the addProvider anymore (its even not possible anymore), but when we remove this lines it result in a bad request url: Base URL: xxxx.com/rest instead of xxxx.com/api/animals/rest, so the value of X-Forward-Prefix is still missing. I already tried configuring pathMapping(path) on the Docket but this results in: xxxx.com/rest/api/animals, same for :

     .pathProvider(new DefaultPathProvider() {
            @Override
            public String getDocumentationPath() {
                return "/api/animals/";
            }
        });

This results in: [ Base URL: xxxx.com/rest ] --> WRONG, should be: https://xxxx.com/api/animals/rest/ https://xxxx.com/api/animals/rest/v2/api-docs. --> correct

Thanks for your help!

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:6 (1 by maintainers)

github_iconTop GitHub Comments

4reactions
atetznercommented, Jul 28, 2020

Just found a solution to set the basePath programmatically. In my case, I use the X-Forwarded-* headers from upstream HTTP proxies, but setting the basePath statically is also no problem. Base for this solution are Springfox Plugins that allow manipulation of the generated swagger.

Kotlin:

import com.daimler.gssnplus.libraries.util.orNull
import io.swagger.models.Swagger
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.stereotype.Component
import springfox.documentation.spi.DocumentationType
import springfox.documentation.swagger.common.HostNameProvider.componentsFrom
import springfox.documentation.swagger2.web.SwaggerTransformationContext
import springfox.documentation.swagger2.web.WebMvcSwaggerTransformationFilter
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletRequestWrapper

/*
 * Andreas Tetzner <andreas.tetzner@daimler.com>, Daimler TSS GmbH
 * Imprint: https://github.com/Daimler/daimler-foss/blob/master/LEGAL_IMPRINT.md
 */

/**
 * This class will set the basePath for the generated Swagger documentation, respecting `X-Forwarded-*` headers from
 * upstream proxies. This class fixes the problem, that for Spring HATEOAS, it is required to have a
 * [ForwardedHeaderFilter] in the list of request filters; this filter hides the `X-Forwarded-*` headers causing the
 * Springfox Swagger's [WebMvcBasePathAndHostnameTransformationFilter] to be not reliably able to define the `basePath`
 * for the generated Swagger. To work around the hidden headers, this class will traverse up the chain of
 * [HttpServletRequestWrapper]s until the root [HttpServletRequest] is found, which contains *all* headers. This
 * request is then used to build the `basePath` for the Swagger documentation.
 */
@Component
@ConditionalOnClass(WebMvcSwaggerTransformationFilter::class)
@Order(Ordered.LOWEST_PRECEDENCE)
class SpringfoxSwaggerBasePathResolver : WebMvcSwaggerTransformationFilter {

    override fun transform(context: SwaggerTransformationContext<HttpServletRequest>): Swagger {
        val swagger = context.specification

        var httpRequest = context.request().orNull() ?: return swagger
        while (httpRequest is HttpServletRequestWrapper) {
            val servletRequest = httpRequest.request
            if (servletRequest is HttpServletRequest)
                httpRequest = servletRequest
            else
                break
        }

        val uriComponents = componentsFrom(httpRequest, swagger.basePath)
        val basePath = uriComponents
            .path
            .takeIf {
                it?.isNotBlank() == true
            }
            ?: "/"

        val contextPath = httpRequest.contextPath
        swagger.basePath = contextPath
            ?.let {
                basePath.replace(it, "")
            }
            ?: basePath

        return swagger
    }

    override fun supports(delimiter: DocumentationType): Boolean = delimiter == DocumentationType.SWAGGER_2
}
1reaction
yuebaixcommented, Oct 5, 2021

This is my solution in java.Hope to be helpful.

@Component
public class WebMvcXForwardedPrefixOpenApiTransformationFilter implements WebMvcOpenApiTransformationFilter {
    private static final String X_FORWARDED_PREFIX = "X-Forwarded-Prefix";
    private static final String COMMA = ",";

    @Override
    public OpenAPI transform(OpenApiTransformationContext<HttpServletRequest> context) {
        OpenAPI openApi = context.getSpecification();
        context.request().ifPresent(httpServletRequest -> {
            String xForwardedPrefix = httpServletRequest.getHeader(X_FORWARDED_PREFIX);
            if (!StringUtils.isEmpty(xForwardedPrefix)) {
                String[] prefixArr = xForwardedPrefix.split(COMMA);
                if (prefixArr.length > 0) {
                    String prefix = fixup(prefixArr[0]);
                    List<Server> servers = openApi.getServers();
                    for (Server server : servers) {
                        UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(server.getUrl());
                        uriComponentsBuilder.path(prefix);
                        UriComponents uriComponents = uriComponentsBuilder.build();
                        server.setUrl(uriComponents.toUriString());
                    }
                }
            }
        });
        return openApi;
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return delimiter == DocumentationType.OAS_30;
    }

    private String fixup(String path) {
        if (StringUtils.isEmpty(path)
                || "/".equals(path)
                || "//".equals(path)) {
            return "/";
        }
        return StringUtils.trimTrailingCharacter(path.replace("//", "/"), '/');
    }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

nginx reverse proxy + Spring ResourceSupport produces ...
I am using Tomcat with Spring and NGINX as a reverse proxy. Spring generated URL: http://localhost:8080/spring-ng-seed. Project URL: https:// ...
Read more >
Using the Forwarded header - NGINX
NGINX configuration and caveats for deploying the Forwarded header.
Read more >
Proxy Reference - v3.1.x - Kong Docs
Route : This refers to the Kong Gateway routes entity. ... By default, Kong Gateway would then proxy the request upstream without changing...
Read more >
https://raw.githubusercontent.com/kubernetes/ingre...
... not working in ewma load-balance - [X] [#6550](https://github.com/kubernetes/ingress-nginx/pull/6550) Ensure any change in the charts directory trigger ...
Read more >
F.A.Q - Springdoc-openapi
How can I define multiple OpenAPI definitions in one Spring Boot project? ... and as implementation Jersey ( @Path for example), we do...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found