Skip to content

Commit 970d9f0

Browse files
committed
Abstract shared Swagger configurer logic
1 parent 9962081 commit 970d9f0

File tree

4 files changed

+287
-134
lines changed

4 files changed

+287
-134
lines changed

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,11 @@ public final class Constants {
376376
*/
377377
public static final String SWAGGER_INITIALIZER_PATTERN = "/*" + SWAGGER_INITIALIZER_JS;
378378

379+
/**
380+
* The constant SWAGGER_RESOURCE_CACHE_NAME.
381+
*/
382+
public static final String SWAGGER_RESOURCE_CACHE_NAME = "swagger-resource-chain-cache";
383+
379384
/**
380385
* The constant HEALTH_PATTERN.
381386
*/
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package org.springdoc.ui;
2+
3+
import org.springdoc.core.properties.SwaggerUiConfigProperties;
4+
import org.springframework.boot.autoconfigure.web.WebProperties;
5+
import org.springframework.web.util.pattern.PathPattern;
6+
import org.springframework.web.util.pattern.PathPatternParser;
7+
8+
import java.util.Arrays;
9+
10+
import static org.springdoc.core.utils.Constants.ALL_PATTERN;
11+
import static org.springdoc.core.utils.Constants.SWAGGER_INITIALIZER_PATTERN;
12+
import static org.springdoc.core.utils.Constants.SWAGGER_UI_PREFIX;
13+
import static org.springdoc.core.utils.Constants.SWAGGER_UI_WEBJAR_NAME;
14+
import static org.springdoc.core.utils.Constants.SWAGGER_UI_WEBJAR_NAME_PATTERN;
15+
import static org.springdoc.core.utils.Constants.WEBJARS_RESOURCE_LOCATION;
16+
import static org.springframework.util.AntPathMatcher.DEFAULT_PATH_SEPARATOR;
17+
18+
/**
19+
* The type Abstract swagger configurer.
20+
*/
21+
public abstract class AbstractSwaggerConfigurer {
22+
23+
/**
24+
* The Swagger ui config properties.
25+
*/
26+
private final SwaggerUiConfigProperties swaggerUiConfigProperties;
27+
28+
/**
29+
* The Spring Web config properties.
30+
*/
31+
private final WebProperties springWebProperties;
32+
33+
/**
34+
* The path pattern parser.
35+
*/
36+
private final PathPatternParser parser = new PathPatternParser();
37+
38+
/**
39+
* Instantiates a new Abstract swagger configurer.
40+
*
41+
* @param swaggerUiConfigProperties the swagger ui calculated config
42+
* @param springWebProperties the spring web config
43+
*/
44+
protected AbstractSwaggerConfigurer(SwaggerUiConfigProperties swaggerUiConfigProperties, WebProperties springWebProperties) {
45+
this.swaggerUiConfigProperties = swaggerUiConfigProperties;
46+
this.springWebProperties = springWebProperties;
47+
}
48+
49+
/**
50+
* Gets the handler configs for mapping Swagger UI resources.
51+
*
52+
* @return the Swagger UI handler configs.
53+
*/
54+
protected SwaggerResourceHandlerConfig[] getSwaggerHandlerConfigs() {
55+
String swaggerUiPattern = getUiRootPath() + SWAGGER_UI_PREFIX + ALL_PATTERN;
56+
String swaggerUiInitializerPattern = combinePatterns(swaggerUiPattern, SWAGGER_INITIALIZER_PATTERN);
57+
String swaggerUiResourceLocation = WEBJARS_RESOURCE_LOCATION + SWAGGER_UI_WEBJAR_NAME + DEFAULT_PATH_SEPARATOR +
58+
swaggerUiConfigProperties.getVersion() + DEFAULT_PATH_SEPARATOR;
59+
60+
return new SwaggerResourceHandlerConfig[]{
61+
SwaggerResourceHandlerConfig.createCached(swaggerUiPattern, swaggerUiResourceLocation),
62+
SwaggerResourceHandlerConfig.createUncached(swaggerUiInitializerPattern, swaggerUiResourceLocation)
63+
};
64+
}
65+
66+
/**
67+
* Gets the handler configs for mapping webjar resources for the Swagger UI.
68+
*
69+
* @return the Swagger UI webjar handler configs.
70+
*/
71+
protected SwaggerResourceHandlerConfig[] getSwaggerWebjarHandlerConfigs() {
72+
if (!springWebProperties.getResources().isAddMappings()) return new SwaggerResourceHandlerConfig[]{};
73+
74+
String swaggerUiWebjarPattern = combinePatterns(getWebjarsPathPattern(), SWAGGER_UI_WEBJAR_NAME_PATTERN) + ALL_PATTERN;
75+
String swaggerUiWebjarInitializerPattern = combinePatterns(swaggerUiWebjarPattern, SWAGGER_INITIALIZER_PATTERN);
76+
String swaggerUiWebjarResourceLocation = WEBJARS_RESOURCE_LOCATION;
77+
78+
return new SwaggerResourceHandlerConfig[]{
79+
SwaggerResourceHandlerConfig.createCached(swaggerUiWebjarPattern, swaggerUiWebjarResourceLocation),
80+
SwaggerResourceHandlerConfig.createUncached(swaggerUiWebjarInitializerPattern, swaggerUiWebjarResourceLocation)
81+
};
82+
}
83+
84+
/**
85+
* Gets the root path for the Swagger UI.
86+
*
87+
* @return the Swagger UI root path.
88+
*/
89+
protected abstract String getUiRootPath();
90+
91+
/**
92+
* Gets the path pattern for webjar resources.
93+
*
94+
* @return the webjars path pattern.
95+
*/
96+
protected abstract String getWebjarsPathPattern();
97+
98+
/**
99+
* Combines pattern strings into a new pattern according to the rules of {@link PathPattern#combine}.
100+
*
101+
* <p>For example:
102+
* <ul>
103+
* <li><code>/webjars/&#42;&#42;</code> + <code>/swagger-ui/&#42;&#42;</code> => <code>/webjars/swagger-ui/&#42;&#42;</code></li>
104+
* <li><code>/documentation/swagger-ui&#42;/&#42;&#42;</code> + <code>/&#42;.js</code> => <code>/documentation/swagger-ui&#42;/&#42;.js</code></li>
105+
* </ul>
106+
*
107+
* @param patterns the patterns to merge
108+
*
109+
* @return the combination of the patterns strings
110+
*
111+
* @throws IllegalArgumentException if the patterns cannot be combined
112+
*
113+
* @see PathPattern#combine
114+
*/
115+
protected String combinePatterns(String... patterns) {
116+
return Arrays.stream(patterns)
117+
.map(this::parsePattern)
118+
.reduce(PathPattern::combine)
119+
.map(PathPattern::getPatternString)
120+
.orElseThrow(IllegalArgumentException::new);
121+
}
122+
123+
private PathPattern parsePattern(String pattern) {
124+
return parser.parse(parser.initFullPathPattern(pattern));
125+
}
126+
127+
/**
128+
* The type Swagger resource handler config.
129+
*
130+
* @param cacheResources whether to cache resources.
131+
* @param pattern the pattern to match.
132+
* @param locations the locations to use.
133+
*/
134+
protected record SwaggerResourceHandlerConfig(boolean cacheResources, String pattern, String... locations) {
135+
136+
/**
137+
* Create a Swagger resource handler config.
138+
*
139+
* @param cacheResources whether to cache resources.
140+
* @param pattern the pattern to match.
141+
* @param locations the locations to use.
142+
*/
143+
public static SwaggerResourceHandlerConfig create(boolean cacheResources, String pattern, String... locations) {
144+
return new SwaggerResourceHandlerConfig(cacheResources, pattern, locations);
145+
}
146+
147+
/**
148+
* Create a Swagger resource handler config with resource caching enabled.
149+
*
150+
* @param pattern the pattern to match.
151+
* @param locations the locations to use.
152+
*/
153+
public static SwaggerResourceHandlerConfig createCached(String pattern, String... locations) {
154+
return create(true, pattern, locations);
155+
}
156+
157+
/**
158+
* Create a Swagger resource handler config with resource caching disabled.
159+
*
160+
* @param pattern the pattern to match.
161+
* @param locations the locations to use.
162+
*/
163+
public static SwaggerResourceHandlerConfig createUncached(String pattern, String... locations) {
164+
return create(false, pattern, locations);
165+
}
166+
}
167+
}

springdoc-openapi-starter-webflux-ui/src/main/java/org/springdoc/webflux/ui/SwaggerWebFluxConfigurer.java

Lines changed: 58 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,26 @@
2929
import org.springdoc.core.properties.SwaggerUiConfigParameters;
3030
import org.springdoc.core.properties.SwaggerUiConfigProperties;
3131

32+
import org.springdoc.ui.AbstractSwaggerConfigurer;
3233
import org.springframework.boot.autoconfigure.web.WebProperties;
3334
import org.springframework.boot.webflux.autoconfigure.WebFluxProperties;
35+
import org.springframework.cache.Cache;
36+
import org.springframework.cache.concurrent.ConcurrentMapCache;
3437
import org.springframework.http.CacheControl;
38+
import org.springframework.web.reactive.config.ResourceChainRegistration;
39+
import org.springframework.web.reactive.config.ResourceHandlerRegistration;
3540
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
3641
import org.springframework.web.reactive.config.WebFluxConfigurer;
37-
import org.springframework.web.util.pattern.PathPattern;
38-
import org.springframework.web.util.pattern.PathPatternParser;
39-
import static org.springdoc.core.utils.Constants.ALL_PATTERN;
40-
import static org.springdoc.core.utils.Constants.SWAGGER_INITIALIZER_PATTERN;
41-
import static org.springdoc.core.utils.Constants.SWAGGER_UI_PREFIX;
42-
import static org.springdoc.core.utils.Constants.SWAGGER_UI_WEBJAR_NAME;
43-
import static org.springdoc.core.utils.Constants.SWAGGER_UI_WEBJAR_NAME_PATTERN;
44-
import static org.springdoc.core.utils.Constants.WEBJARS_RESOURCE_LOCATION;
45-
import static org.springframework.util.AntPathMatcher.DEFAULT_PATH_SEPARATOR;
42+
import org.springframework.web.reactive.resource.CachingResourceResolver;
43+
44+
import static org.springdoc.core.utils.Constants.SWAGGER_RESOURCE_CACHE_NAME;
4645

4746
/**
4847
* The type Swagger web flux configurer.
4948
*
5049
* @author bnasslahsen
5150
*/
52-
public class SwaggerWebFluxConfigurer implements WebFluxConfigurer {
51+
public class SwaggerWebFluxConfigurer extends AbstractSwaggerConfigurer implements WebFluxConfigurer {
5352

5453
/**
5554
* The Swagger index transformer.
@@ -66,11 +65,6 @@ public class SwaggerWebFluxConfigurer implements WebFluxConfigurer {
6665
*/
6766
private final SwaggerUiConfigProperties swaggerUiConfigProperties;
6867

69-
/**
70-
* The Spring Web config properties.
71-
*/
72-
private final WebProperties springWebProperties;
73-
7468
/**
7569
* The Spring WebFlux config properties.
7670
*/
@@ -81,7 +75,10 @@ public class SwaggerWebFluxConfigurer implements WebFluxConfigurer {
8175
*/
8276
private final SwaggerWelcomeCommon swaggerWelcomeCommon;
8377

84-
private final PathPatternParser parser = new PathPatternParser();
78+
/**
79+
* The Swagger resource chain cache.
80+
*/
81+
private Cache cache;
8582

8683
/**
8784
* Instantiates a new Swagger web flux configurer.
@@ -91,90 +88,84 @@ public class SwaggerWebFluxConfigurer implements WebFluxConfigurer {
9188
* @param springWebFluxProperties the spring webflux config
9289
* @param swaggerIndexTransformer the swagger index transformer
9390
* @param swaggerResourceResolver the swagger resource resolver
94-
* @param swaggerWelcomeCommon the swagger welcome common
91+
* @param swaggerWelcomeCommon the swagger welcome common
9592
*/
9693
public SwaggerWebFluxConfigurer(SwaggerUiConfigProperties swaggerUiConfigProperties,
9794
WebProperties springWebProperties, WebFluxProperties springWebFluxProperties,
9895
SwaggerIndexTransformer swaggerIndexTransformer, SwaggerResourceResolver swaggerResourceResolver,
9996
SwaggerWelcomeCommon swaggerWelcomeCommon) {
97+
super(swaggerUiConfigProperties, springWebProperties);
10098
this.swaggerIndexTransformer = swaggerIndexTransformer;
10199
this.swaggerResourceResolver = swaggerResourceResolver;
102100
this.swaggerUiConfigProperties = swaggerUiConfigProperties;
103-
this.springWebProperties = springWebProperties;
104101
this.springWebFluxProperties = springWebFluxProperties;
105102
this.swaggerWelcomeCommon = swaggerWelcomeCommon;
106103
}
107104

108105
@Override
109106
public void addResourceHandlers(ResourceHandlerRegistry registry) {
110-
String swaggerUiPattern = getUiRootPath() + SWAGGER_UI_PREFIX + ALL_PATTERN;
111-
String swaggerUiResourceLocation = WEBJARS_RESOURCE_LOCATION + SWAGGER_UI_WEBJAR_NAME + DEFAULT_PATH_SEPARATOR +
112-
swaggerUiConfigProperties.getVersion() + DEFAULT_PATH_SEPARATOR;
113-
114-
addSwaggerUiResourceHandler(registry, swaggerUiPattern, swaggerUiResourceLocation);
115-
116-
// Add custom mappings for Swagger UI WebJar resources if Spring resource mapping is enabled
117-
if (springWebProperties.getResources().isAddMappings()) {
118-
String webjarsPathPattern = springWebFluxProperties.getWebjarsPathPattern();
119-
120-
String swaggerUiWebjarPattern = mergePatterns(webjarsPathPattern, SWAGGER_UI_WEBJAR_NAME_PATTERN) + ALL_PATTERN;
121-
String swaggerUiWebjarResourceLocation = WEBJARS_RESOURCE_LOCATION;
122-
123-
addSwaggerUiResourceHandler(registry, swaggerUiWebjarPattern, swaggerUiWebjarResourceLocation);
124-
}
107+
addSwaggerResourceHandlers(registry, getSwaggerHandlerConfigs());
108+
addSwaggerResourceHandlers(registry, getSwaggerWebjarHandlerConfigs());
125109
}
126110

127111
/**
128-
* Adds the resource handlers for serving the Swagger UI resources.
112+
* Add resource handlers that use the Swagger resource resolver and transformer.
113+
*
114+
* @param registry the resource handler registry.
115+
* @param handlerConfigs the swagger handler configs.
129116
*/
130-
protected void addSwaggerUiResourceHandler(ResourceHandlerRegistry registry, String pattern, String... resourceLocations) {
131-
registry.addResourceHandler(pattern)
132-
.addResourceLocations(resourceLocations)
133-
.resourceChain(false)
134-
.addResolver(swaggerResourceResolver)
135-
.addTransformer(swaggerIndexTransformer);
136-
137-
// Ensure Swagger initializer has "no-store" Cache-Control header
138-
registry.addResourceHandler(mergePatterns(pattern, SWAGGER_INITIALIZER_PATTERN))
139-
.setCacheControl(CacheControl.noStore())
140-
.addResourceLocations(resourceLocations)
141-
.resourceChain(false)
142-
.addResolver(swaggerResourceResolver)
143-
.addTransformer(swaggerIndexTransformer);
117+
protected void addSwaggerResourceHandlers(ResourceHandlerRegistry registry, SwaggerResourceHandlerConfig... handlerConfigs) {
118+
for (SwaggerResourceHandlerConfig handlerConfig : handlerConfigs) {
119+
addSwaggerResourceHandler(registry, handlerConfig);
120+
}
144121
}
145122

146123
/**
147-
* Computes and returns the root path for the Swagger UI.
124+
* Add a resource handler that uses the Swagger resource resolver and transformer.
148125
*
149-
* @return the Swagger UI root path.
126+
* @param registry the resource handler registry.
127+
* @param handlerConfig the swagger handler config.
150128
*/
129+
protected void addSwaggerResourceHandler(ResourceHandlerRegistry registry, SwaggerResourceHandlerConfig handlerConfig) {
130+
ResourceHandlerRegistration handlerRegistration = registry.addResourceHandler(handlerConfig.pattern());
131+
handlerRegistration.addResourceLocations(handlerConfig.locations());
132+
133+
ResourceChainRegistration chainRegistration;
134+
if (handlerConfig.cacheResources()) {
135+
chainRegistration = handlerRegistration.resourceChain(true, getCache());
136+
} else {
137+
handlerRegistration.setUseLastModified(false);
138+
handlerRegistration.setCacheControl(CacheControl.noStore());
139+
140+
chainRegistration = handlerRegistration.resourceChain(false);
141+
chainRegistration.addResolver(new CachingResourceResolver(getCache())); // only use cache for resolving
142+
}
143+
144+
chainRegistration.addResolver(swaggerResourceResolver);
145+
chainRegistration.addTransformer(swaggerIndexTransformer);
146+
}
147+
148+
@Override
151149
protected String getUiRootPath() {
152150
SwaggerUiConfigParameters swaggerUiConfigParameters = new SwaggerUiConfigParameters(swaggerUiConfigProperties);
153151
swaggerWelcomeCommon.calculateUiRootPath(swaggerUiConfigParameters);
154152

155153
return swaggerUiConfigParameters.getUiRootPath();
156154
}
157155

156+
@Override
157+
protected String getWebjarsPathPattern() {
158+
return springWebFluxProperties.getWebjarsPathPattern();
159+
}
160+
158161
/**
159-
* Combines two patterns into a new pattern according to the rules of {@link PathPattern#combine}.
160-
*
161-
* <p>For example:
162-
* <ul>
163-
* <li><code>/webjars/&#42;&#42;</code> + <code>/swagger-ui/&#42;&#42;</code> => <code>/webjars/swagger-ui/&#42;&#42;</code></li>
164-
* <li><code>/documentation/swagger-ui&#42;/&#42;&#42;</code> + <code>/&#42;.js</code> => <code>/documentation/swagger-ui&#42;/&#42;.js</code></li>
165-
* </ul>
166-
*
167-
* @param pattern1 the first pattern
168-
* @param pattern2 the second pattern
169-
*
170-
* @return the combination of the two patterns
162+
* Gets the Swagger resource chain cache.
171163
*
172-
* @see PathPattern#combine
164+
* @return the cache.
173165
*/
174-
private String mergePatterns(String pattern1, String pattern2) {
175-
PathPattern pathPattern1 = parser.parse(parser.initFullPathPattern(pattern1));
176-
PathPattern pathPattern2 = parser.parse(parser.initFullPathPattern(pattern2));
166+
protected Cache getCache() {
167+
if (cache == null) cache = new ConcurrentMapCache(SWAGGER_RESOURCE_CACHE_NAME);
177168

178-
return pathPattern1.combine(pathPattern2).getPatternString();
169+
return cache;
179170
}
180171
}

0 commit comments

Comments
 (0)