Skip to content

Commit 9ae7f85

Browse files
committed
Add support for addPathPrefix with Webflux swagger-ui. Fixes #1050.
1 parent 156e47a commit 9ae7f85

File tree

9 files changed

+219
-8
lines changed

9 files changed

+219
-8
lines changed

springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiResource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public abstract class OpenApiResource extends AbstractOpenApiResource {
7272
/**
7373
* The Request mapping handler mapping.
7474
*/
75-
private final RequestMappingInfoHandlerMapping requestMappingHandlerMapping;
75+
protected final RequestMappingInfoHandlerMapping requestMappingHandlerMapping;
7676

7777
/**
7878
* Instantiates a new Open api resource.

springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiWebfluxResource.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@
2121
package org.springdoc.webflux.api;
2222

2323
import java.util.List;
24+
import java.util.Map;
2425
import java.util.Optional;
26+
import java.util.function.Predicate;
2527

2628
import com.fasterxml.jackson.core.JsonProcessingException;
2729
import io.swagger.v3.oas.annotations.Operation;
30+
import org.apache.commons.lang3.StringUtils;
2831
import org.springdoc.core.AbstractRequestService;
2932
import org.springdoc.core.ActuatorProvider;
3033
import org.springdoc.core.GenericResponseService;
@@ -43,6 +46,7 @@
4346
import org.springframework.web.bind.annotation.GetMapping;
4447
import org.springframework.web.bind.annotation.RestController;
4548
import org.springframework.web.reactive.result.method.RequestMappingInfoHandlerMapping;
49+
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
4650

4751
import static org.springdoc.core.Constants.API_DOCS_URL;
4852
import static org.springdoc.core.Constants.APPLICATION_OPENAPI_YAML;
@@ -127,7 +131,13 @@ public Mono<String> openapiYaml(ServerHttpRequest serverHttpRequest,
127131
@Override
128132
protected String getServerUrl(ServerHttpRequest serverHttpRequest, String apiDocsUrl) {
129133
String requestUrl = decode(serverHttpRequest.getURI().toString());
130-
return requestUrl.substring(0, requestUrl.length() - apiDocsUrl.length());
134+
Map<String, Predicate<Class<?>>> paths = ((RequestMappingHandlerMapping) requestMappingHandlerMapping).getPathPrefixes();
135+
final String[] prefix = { StringUtils.EMPTY };
136+
paths.forEach((path, classPredicate) -> {
137+
if (classPredicate.test(this.getClass()))
138+
prefix[0] = path;
139+
});
140+
return requestUrl.substring(0, requestUrl.length() - apiDocsUrl.length()- prefix[0].length());
131141
}
132142

133143
}

springdoc-openapi-webflux-ui/src/main/java/org/springdoc/webflux/ui/SwaggerConfig.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.springframework.context.annotation.Configuration;
4545
import org.springframework.context.annotation.Lazy;
4646
import org.springframework.web.reactive.config.WebFluxConfigurer;
47+
import org.springframework.web.reactive.result.method.RequestMappingInfoHandlerMapping;
4748

4849
import static org.springdoc.core.Constants.SPRINGDOC_SWAGGER_UI_ENABLED;
4950
import static org.springdoc.core.Constants.SPRINGDOC_USE_MANAGEMENT_PORT;
@@ -66,13 +67,14 @@ public class SwaggerConfig implements WebFluxConfigurer {
6667
* @param springDocConfigProperties the spring doc config properties
6768
* @param swaggerUiConfigParameters the swagger ui config parameters
6869
* @param optionalWebFluxProperties the optional web flux properties
70+
* @param requestMappingHandlerMapping the request mapping handler mapping
6971
* @return the swagger welcome web flux
7072
*/
7173
@Bean
7274
@ConditionalOnMissingBean
7375
@ConditionalOnProperty(name = SPRINGDOC_USE_MANAGEMENT_PORT, havingValue = "false", matchIfMissing = true)
74-
SwaggerWelcomeWebFlux swaggerWelcome(SwaggerUiConfigProperties swaggerUiConfig, SpringDocConfigProperties springDocConfigProperties,SwaggerUiConfigParameters swaggerUiConfigParameters, Optional<WebFluxProperties> optionalWebFluxProperties) {
75-
return new SwaggerWelcomeWebFlux(swaggerUiConfig,springDocConfigProperties,swaggerUiConfigParameters,optionalWebFluxProperties);
76+
SwaggerWelcomeWebFlux swaggerWelcome(SwaggerUiConfigProperties swaggerUiConfig, SpringDocConfigProperties springDocConfigProperties,SwaggerUiConfigParameters swaggerUiConfigParameters, Optional<WebFluxProperties> optionalWebFluxProperties, RequestMappingInfoHandlerMapping requestMappingHandlerMapping) {
77+
return new SwaggerWelcomeWebFlux(swaggerUiConfig,springDocConfigProperties,swaggerUiConfigParameters,optionalWebFluxProperties,requestMappingHandlerMapping);
7678
}
7779

7880
/**

springdoc-openapi-webflux-ui/src/main/java/org/springdoc/webflux/ui/SwaggerWelcomeWebFlux.java

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,14 @@
2020

2121
package org.springdoc.webflux.ui;
2222

23+
import java.util.ArrayList;
24+
import java.util.List;
2325
import java.util.Map;
26+
import java.util.Map.Entry;
2427
import java.util.Optional;
28+
import java.util.Set;
29+
30+
import javax.annotation.PostConstruct;
2531

2632
import io.swagger.v3.oas.annotations.Operation;
2733
import org.apache.commons.lang3.StringUtils;
@@ -35,12 +41,19 @@
3541
import org.springframework.http.server.reactive.ServerHttpRequest;
3642
import org.springframework.http.server.reactive.ServerHttpResponse;
3743
import org.springframework.stereotype.Controller;
44+
import org.springframework.util.CollectionUtils;
3845
import org.springframework.web.bind.annotation.GetMapping;
3946
import org.springframework.web.bind.annotation.ResponseBody;
47+
import org.springframework.web.method.HandlerMethod;
48+
import org.springframework.web.reactive.result.condition.PatternsRequestCondition;
49+
import org.springframework.web.reactive.result.method.RequestMappingInfo;
50+
import org.springframework.web.reactive.result.method.RequestMappingInfoHandlerMapping;
4051
import org.springframework.web.util.UriComponentsBuilder;
52+
import org.springframework.web.util.pattern.PathPattern;
4153

4254
import static org.springdoc.core.Constants.SWAGGER_CONFIG_URL;
4355
import static org.springdoc.core.Constants.SWAGGER_UI_PATH;
56+
import static org.springdoc.core.Constants.SWAGGGER_CONFIG_FILE;
4457

4558
/**
4659
* The type Swagger welcome.
@@ -52,7 +65,22 @@ public class SwaggerWelcomeWebFlux extends SwaggerWelcomeCommon {
5265
/**
5366
* The Webflux base path.
5467
*/
55-
private String webfluxBasePath = StringUtils.EMPTY ;
68+
private String webfluxBasePath = StringUtils.EMPTY;
69+
70+
/**
71+
* The Swagger config url.
72+
*/
73+
private String swaggerConfigUrl;
74+
75+
/**
76+
* The Api docs url.
77+
*/
78+
private String apiDocsUrl;
79+
80+
/**
81+
* The Request mapping handler mapping.
82+
*/
83+
private final RequestMappingInfoHandlerMapping requestMappingHandlerMapping;
5684

5785
/**
5886
* Instantiates a new Swagger welcome.
@@ -61,12 +89,36 @@ public class SwaggerWelcomeWebFlux extends SwaggerWelcomeCommon {
6189
* @param springDocConfigProperties the spring doc config properties
6290
* @param swaggerUiConfigParameters the swagger ui config parameters
6391
* @param webFluxPropertiesOptional the web flux properties
92+
* @param requestMappingHandlerMapping the request mapping handler mapping
6493
*/
65-
public SwaggerWelcomeWebFlux(SwaggerUiConfigProperties swaggerUiConfig, SpringDocConfigProperties springDocConfigProperties, SwaggerUiConfigParameters swaggerUiConfigParameters, Optional<WebFluxProperties> webFluxPropertiesOptional) {
94+
public SwaggerWelcomeWebFlux(SwaggerUiConfigProperties swaggerUiConfig, SpringDocConfigProperties springDocConfigProperties, SwaggerUiConfigParameters swaggerUiConfigParameters,
95+
Optional<WebFluxProperties> webFluxPropertiesOptional, RequestMappingInfoHandlerMapping requestMappingHandlerMapping) {
6696
super(swaggerUiConfig, springDocConfigProperties, swaggerUiConfigParameters);
97+
this.requestMappingHandlerMapping = requestMappingHandlerMapping;
6798
webFluxPropertiesOptional.ifPresent(webFluxProperties -> webfluxBasePath = webFluxProperties.getBasePath());
6899
}
69100

101+
/**
102+
* Init.
103+
*/
104+
@PostConstruct
105+
private void init() {
106+
Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
107+
List<Entry<RequestMappingInfo, HandlerMethod>> entries = new ArrayList<>(map.entrySet());
108+
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : entries) {
109+
RequestMappingInfo requestMappingInfo = entry.getKey();
110+
PatternsRequestCondition patternsRequestCondition = requestMappingInfo.getPatternsCondition();
111+
Set<PathPattern> patterns = patternsRequestCondition.getPatterns();
112+
for (PathPattern pathPattern : patterns) {
113+
String operationPath = pathPattern.getPatternString();
114+
if (operationPath.endsWith(SWAGGGER_CONFIG_FILE))
115+
swaggerConfigUrl = StringUtils.defaultString(webfluxBasePath) + operationPath;
116+
else if (operationPath.endsWith(springDocConfigProperties.getApiDocs().getPath()))
117+
apiDocsUrl = StringUtils.defaultString(webfluxBasePath) + operationPath;
118+
}
119+
}
120+
}
121+
70122
/**
71123
* Redirect to ui mono.
72124
*
@@ -101,6 +153,23 @@ protected void calculateUiRootPath(StringBuilder... sbUrls) {
101153
calculateUiRootCommon(sbUrl, sbUrls);
102154
}
103155

156+
@Override
157+
protected void buildConfigUrl(String contextPath, UriComponentsBuilder uriComponentsBuilder) {
158+
if (StringUtils.isEmpty(swaggerUiConfig.getConfigUrl())) {
159+
swaggerUiConfigParameters.setConfigUrl(swaggerConfigUrl);
160+
if (CollectionUtils.isEmpty(swaggerUiConfigParameters.getUrls())) {
161+
String swaggerUiUrl = swaggerUiConfig.getUrl();
162+
if (StringUtils.isEmpty(swaggerUiUrl))
163+
swaggerUiConfigParameters.setUrl(apiDocsUrl);
164+
else
165+
swaggerUiConfigParameters.setUrl(swaggerUiUrl);
166+
}
167+
else
168+
swaggerUiConfigParameters.addUrl(apiDocsUrl);
169+
}
170+
calculateOauth2RedirectUrl(uriComponentsBuilder);
171+
}
172+
104173
@Override
105174
protected void calculateOauth2RedirectUrl(UriComponentsBuilder uriComponentsBuilder) {
106175
if (oauthPrefix == null && !swaggerUiConfigParameters.isValidUrl(swaggerUiConfigParameters.getOauth2RedirectUrl())) {

springdoc-openapi-webflux-ui/src/test/java/test/org/springdoc/ui/app19/SpringDocApp19Test.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ void init(){
5555
}
5656

5757
@Test
58-
public void testIndexActuator() throws Exception {
58+
public void testIndex() throws Exception {
5959
HttpStatus httpStatusMono = webClient.get().uri("/")
6060
.exchangeToMono(clientResponse -> Mono.just(clientResponse.statusCode())).block();
6161
assertTrue(httpStatusMono.equals(HttpStatus.TEMPORARY_REDIRECT));
@@ -66,7 +66,6 @@ public void testIndexActuator() throws Exception {
6666

6767
String contentAsString = webClient.get().uri("/v3/api-docs/swagger-config").retrieve()
6868
.bodyToMono(String.class).block();
69-
System.out.println(contentAsString);
7069
String expected = getContent("results/app19-1.json");
7170
assertEquals(expected, contentAsString, true);
7271
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.ui.app20;
20+
21+
22+
import org.springframework.web.bind.annotation.GetMapping;
23+
import org.springframework.web.bind.annotation.RequestMapping;
24+
import org.springframework.web.bind.annotation.RestController;
25+
26+
27+
@RequestMapping("/foo/bar")
28+
@RestController
29+
public class HelloController {
30+
31+
@GetMapping("/test")
32+
public String doSomethingInteresting() {
33+
return "OK";
34+
}
35+
36+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.ui.app20;
20+
21+
22+
import javax.annotation.PostConstruct;
23+
24+
import org.junit.jupiter.api.Assertions;
25+
import org.junit.jupiter.api.Test;
26+
import reactor.core.publisher.Mono;
27+
import test.org.springdoc.ui.AbstractCommonTest;
28+
29+
import org.springframework.boot.autoconfigure.SpringBootApplication;
30+
import org.springframework.boot.test.context.SpringBootTest;
31+
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
32+
import org.springframework.boot.web.server.LocalServerPort;
33+
import org.springframework.http.HttpStatus;
34+
import org.springframework.web.reactive.function.client.WebClient;
35+
36+
import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
37+
38+
39+
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT,
40+
properties = { "server.port=9020" })
41+
class SpringDocApp20Test extends AbstractCommonTest {
42+
43+
@LocalServerPort
44+
private int port;
45+
46+
private WebClient webClient;
47+
48+
@SpringBootApplication
49+
static class SpringDocTestApp {}
50+
51+
@PostConstruct
52+
void init(){
53+
webClient = WebClient.builder().baseUrl("http://localhost:"+port)
54+
.build();
55+
}
56+
57+
@Test
58+
public void testIndex() throws Exception {
59+
HttpStatus httpStatusMono = webClient.get().uri("/test/swagger-ui.html")
60+
.exchangeToMono(clientResponse -> Mono.just(clientResponse.statusCode())).block();
61+
Assertions.assertTrue(httpStatusMono.equals(HttpStatus.TEMPORARY_REDIRECT));
62+
63+
httpStatusMono = webClient.get().uri("/webjars/swagger-ui/index.html")
64+
.exchangeToMono(clientResponse -> Mono.just(clientResponse.statusCode())).block();
65+
Assertions.assertTrue(httpStatusMono.equals(HttpStatus.OK));
66+
67+
String contentAsString = webClient.get().uri("/test/v3/api-docs/swagger-config").retrieve()
68+
.bodyToMono(String.class).block();
69+
String expected = getContent("results/app20-1.json");
70+
assertEquals(expected, contentAsString, true);
71+
}
72+
73+
74+
75+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package test.org.springdoc.ui.app20;
2+
3+
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.web.reactive.config.PathMatchConfigurer;
5+
import org.springframework.web.reactive.config.WebFluxConfigurer;
6+
7+
@Configuration
8+
public class WebConfig implements WebFluxConfigurer {
9+
@Override
10+
public void configurePathMatching(PathMatchConfigurer configurer) {
11+
configurer.addPathPrefix("/test", t-> true) ;
12+
}
13+
14+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"configUrl": "/test/v3/api-docs/swagger-config",
3+
"oauth2RedirectUrl": "http://localhost:9020/webjars/swagger-ui/oauth2-redirect.html",
4+
"url": "/test/v3/api-docs",
5+
"validatorUrl": ""
6+
}

0 commit comments

Comments
 (0)