下面列出了怎么用org.springframework.http.server.PathContainer的API类实例代码及写法,或者点击链接到github查看源代码。
protected Mono<Resource> getResource(ServerWebExchange exchange) {
String name = HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE;
PathContainer pathWithinHandler = exchange.getRequiredAttribute(name);
String path = processPath(pathWithinHandler.value());
if (!StringUtils.hasText(path) || isInvalidPath(path)) {
return Mono.empty();
}
if (isInvalidEncodedPath(path)) {
return Mono.empty();
}
Assert.state(this.resolverChain != null, "ResourceResolverChain not initialized");
Assert.state(this.transformerChain != null, "ResourceTransformerChain not initialized");
return this.resolverChain.resolveResource(exchange, path, getLocations())
.flatMap(resource -> this.transformerChain.transform(exchange, resource));
}
private Mono<String> resolveResourceUrl(ServerWebExchange exchange, PathContainer lookupPath) {
return this.handlerMap.entrySet().stream()
.filter(entry -> entry.getKey().matches(lookupPath))
.min((entry1, entry2) ->
PathPattern.SPECIFICITY_COMPARATOR.compare(entry1.getKey(), entry2.getKey()))
.map(entry -> {
PathContainer path = entry.getKey().extractPathWithinPattern(lookupPath);
int endIndex = lookupPath.elements().size() - path.elements().size();
PathContainer mapping = lookupPath.subPath(0, endIndex);
ResourceWebHandler handler = entry.getValue();
List<ResourceResolver> resolvers = handler.getResourceResolvers();
ResourceResolverChain chain = new DefaultResourceResolverChain(resolvers);
return chain.resolveUrlPath(path.value(), handler.getLocations())
.map(resolvedPath -> mapping.value() + resolvedPath);
})
.orElseGet(() ->{
if (logger.isTraceEnabled()) {
logger.trace(exchange.getLogPrefix() + "No match for \"" + lookupPath + "\"");
}
return Mono.empty();
});
}
/**
* Look up a handler instance for the given URL lookup path.
* <p>Supports direct matches, e.g. a registered "/test" matches "/test",
* and various path pattern matches, e.g. a registered "/t*" matches
* both "/test" and "/team". For details, see the PathPattern class.
* @param lookupPath the URL the handler is mapped to
* @param exchange the current exchange
* @return the associated handler instance, or {@code null} if not found
* @see org.springframework.web.util.pattern.PathPattern
*/
@Nullable
protected Object lookupHandler(PathContainer lookupPath, ServerWebExchange exchange) throws Exception {
List<PathPattern> matches = this.handlerMap.keySet().stream()
.filter(key -> key.matches(lookupPath))
.collect(Collectors.toList());
if (matches.isEmpty()) {
return null;
}
if (matches.size() > 1) {
matches.sort(PathPattern.SPECIFICITY_COMPARATOR);
if (logger.isTraceEnabled()) {
logger.debug(exchange.getLogPrefix() + "Matching patterns " + matches);
}
}
PathPattern pattern = matches.get(0);
PathContainer pathWithinMapping = pattern.extractPathWithinPattern(lookupPath);
return handleMatch(this.handlerMap.get(pattern), pattern, pathWithinMapping, exchange);
}
private Object handleMatch(Object handler, PathPattern bestMatch, PathContainer pathWithinMapping,
ServerWebExchange exchange) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, exchange);
exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatch);
exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
return handler;
}
/**
* It checks whether a request should be traced or not.
*
* @return whether request should be traced or not
*/
protected boolean shouldBeTraced(final ServerHttpRequest request) {
final PathContainer pathWithinApplication = request.getPath().pathWithinApplication();
// skip URLs matching skip pattern
// e.g. pattern is defined as '/health|/status' then URL 'http://localhost:5000/context/health' won't be traced
if (skipPattern != null) {
final String url = pathWithinApplication.value();
if (skipPattern.matcher(url).matches()) {
if (LOG.isTraceEnabled()) {
LOG.trace("Not tracing request " + request + " because it matches skip pattern: " + skipPattern);
}
return false;
}
}
if (!urlPatterns.isEmpty() && urlPatterns.stream().noneMatch(urlPattern -> urlPattern.matches(pathWithinApplication))) {
if (LOG.isTraceEnabled()) {
LOG.trace("Not tracing request " + request + " because it does not match any URL pattern: " + urlPatterns);
}
return false;
}
return true;
}
private void testUrl(String url, Object bean, HandlerMapping handlerMapping, String pathWithinMapping) {
MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.GET, URI.create(url)).build();
ServerWebExchange exchange = MockServerWebExchange.from(request);
Object actual = handlerMapping.getHandler(exchange).block();
if (bean != null) {
assertNotNull(actual);
assertSame(bean, actual);
//noinspection OptionalGetWithoutIsPresent
PathContainer path = exchange.getAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
assertNotNull(path);
assertEquals(pathWithinMapping, path.value());
}
else {
assertNull(actual);
}
}
/**
* Whether this pattern matches the given path.
* @param pathContainer the candidate path to attempt to match against
* @return {@code true} if the path matches this pattern
*/
public boolean matches(PathContainer pathContainer) {
if (this.head == null) {
return !hasLength(pathContainer) ||
(this.matchOptionalTrailingSeparator && pathContainerIsJustSeparator(pathContainer));
}
else if (!hasLength(pathContainer)) {
if (this.head instanceof WildcardTheRestPathElement || this.head instanceof CaptureTheRestPathElement) {
pathContainer = EMPTY_PATH; // Will allow CaptureTheRest to bind the variable to empty
}
else {
return false;
}
}
MatchingContext matchingContext = new MatchingContext(pathContainer, false);
return this.head.matches(0, matchingContext);
}
/**
* Match this pattern to the given URI path and return extracted URI template
* variables as well as path parameters (matrix variables).
* @param pathContainer the candidate path to attempt to match against
* @return info object with the extracted variables, or {@code null} for no match
*/
@Nullable
public PathMatchInfo matchAndExtract(PathContainer pathContainer) {
if (this.head == null) {
return hasLength(pathContainer) &&
!(this.matchOptionalTrailingSeparator && pathContainerIsJustSeparator(pathContainer))
? null : PathMatchInfo.EMPTY;
}
else if (!hasLength(pathContainer)) {
if (this.head instanceof WildcardTheRestPathElement || this.head instanceof CaptureTheRestPathElement) {
pathContainer = EMPTY_PATH; // Will allow CaptureTheRest to bind the variable to empty
}
else {
return null;
}
}
MatchingContext matchingContext = new MatchingContext(pathContainer, true);
return this.head.matches(0, matchingContext) ? matchingContext.getPathMatchResult() : null;
}
protected Mono<Resource> getResource(ServerWebExchange exchange) {
String name = HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE;
PathContainer pathWithinHandler = exchange.getRequiredAttribute(name);
String path = processPath(pathWithinHandler.value());
if (!StringUtils.hasText(path) || isInvalidPath(path)) {
return Mono.empty();
}
if (isInvalidEncodedPath(path)) {
return Mono.empty();
}
Assert.state(this.resolverChain != null, "ResourceResolverChain not initialized");
Assert.state(this.transformerChain != null, "ResourceTransformerChain not initialized");
return this.resolverChain.resolveResource(exchange, path, getLocations())
.flatMap(resource -> this.transformerChain.transform(exchange, resource));
}
private Mono<String> resolveResourceUrl(ServerWebExchange exchange, PathContainer lookupPath) {
return this.handlerMap.entrySet().stream()
.filter(entry -> entry.getKey().matches(lookupPath))
.min((entry1, entry2) ->
PathPattern.SPECIFICITY_COMPARATOR.compare(entry1.getKey(), entry2.getKey()))
.map(entry -> {
PathContainer path = entry.getKey().extractPathWithinPattern(lookupPath);
int endIndex = lookupPath.elements().size() - path.elements().size();
PathContainer mapping = lookupPath.subPath(0, endIndex);
ResourceWebHandler handler = entry.getValue();
List<ResourceResolver> resolvers = handler.getResourceResolvers();
ResourceResolverChain chain = new DefaultResourceResolverChain(resolvers);
return chain.resolveUrlPath(path.value(), handler.getLocations())
.map(resolvedPath -> mapping.value() + resolvedPath);
})
.orElseGet(() ->{
if (logger.isTraceEnabled()) {
logger.trace(exchange.getLogPrefix() + "No match for \"" + lookupPath + "\"");
}
return Mono.empty();
});
}
/**
* Look up a handler instance for the given URL lookup path.
* <p>Supports direct matches, e.g. a registered "/test" matches "/test",
* and various path pattern matches, e.g. a registered "/t*" matches
* both "/test" and "/team". For details, see the PathPattern class.
* @param lookupPath the URL the handler is mapped to
* @param exchange the current exchange
* @return the associated handler instance, or {@code null} if not found
* @see org.springframework.web.util.pattern.PathPattern
*/
@Nullable
protected Object lookupHandler(PathContainer lookupPath, ServerWebExchange exchange) throws Exception {
List<PathPattern> matches = this.handlerMap.keySet().stream()
.filter(key -> key.matches(lookupPath))
.collect(Collectors.toList());
if (matches.isEmpty()) {
return null;
}
if (matches.size() > 1) {
matches.sort(PathPattern.SPECIFICITY_COMPARATOR);
if (logger.isTraceEnabled()) {
logger.debug(exchange.getLogPrefix() + "Matching patterns " + matches);
}
}
PathPattern pattern = matches.get(0);
PathContainer pathWithinMapping = pattern.extractPathWithinPattern(lookupPath);
return handleMatch(this.handlerMap.get(pattern), pattern, pathWithinMapping, exchange);
}
private Object handleMatch(Object handler, PathPattern bestMatch, PathContainer pathWithinMapping,
ServerWebExchange exchange) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, exchange);
exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatch);
exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
return handler;
}
private void testUrl(String url, Object bean, HandlerMapping handlerMapping, String pathWithinMapping) {
MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.GET, URI.create(url)).build();
ServerWebExchange exchange = MockServerWebExchange.from(request);
Object actual = handlerMapping.getHandler(exchange).block();
if (bean != null) {
assertNotNull(actual);
assertSame(bean, actual);
//noinspection OptionalGetWithoutIsPresent
PathContainer path = exchange.getAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
assertNotNull(path);
assertEquals(pathWithinMapping, path.value());
}
else {
assertNull(actual);
}
}
/**
* Whether this pattern matches the given path.
* @param pathContainer the candidate path to attempt to match against
* @return {@code true} if the path matches this pattern
*/
public boolean matches(PathContainer pathContainer) {
if (this.head == null) {
return !hasLength(pathContainer) ||
(this.matchOptionalTrailingSeparator && pathContainerIsJustSeparator(pathContainer));
}
else if (!hasLength(pathContainer)) {
if (this.head instanceof WildcardTheRestPathElement || this.head instanceof CaptureTheRestPathElement) {
pathContainer = EMPTY_PATH; // Will allow CaptureTheRest to bind the variable to empty
}
else {
return false;
}
}
MatchingContext matchingContext = new MatchingContext(pathContainer, false);
return this.head.matches(0, matchingContext);
}
/**
* Match this pattern to the given URI path and return extracted URI template
* variables as well as path parameters (matrix variables).
* @param pathContainer the candidate path to attempt to match against
* @return info object with the extracted variables, or {@code null} for no match
*/
@Nullable
public PathMatchInfo matchAndExtract(PathContainer pathContainer) {
if (this.head == null) {
return hasLength(pathContainer) &&
!(this.matchOptionalTrailingSeparator && pathContainerIsJustSeparator(pathContainer))
? null : PathMatchInfo.EMPTY;
}
else if (!hasLength(pathContainer)) {
if (this.head instanceof WildcardTheRestPathElement || this.head instanceof CaptureTheRestPathElement) {
pathContainer = EMPTY_PATH; // Will allow CaptureTheRest to bind the variable to empty
}
else {
return null;
}
}
MatchingContext matchingContext = new MatchingContext(pathContainer, true);
return this.head.matches(0, matchingContext) ? matchingContext.getPathMatchResult() : null;
}
/**
* It checks whether a request should be traced or not.
*
* @return whether request should be traced or not
*/
boolean shouldBeTraced(final ServerHttpRequest request) {
final PathContainer pathWithinApplication = request.getPath().pathWithinApplication();
// skip URLs matching skip pattern
// e.g. pattern is defined as '/health|/status' then URL 'http://localhost:5000/context/health' won't be traced
if (skipPattern != null) {
final String url = pathWithinApplication.value();
if (skipPattern.matcher(url).matches()) {
if (LOG.isTraceEnabled()) {
LOG.trace("Not tracing request " + request + " because it matches skip pattern: " + skipPattern);
}
return false;
}
}
if (!urlPatterns.isEmpty() && urlPatterns.stream().noneMatch(urlPattern -> urlPattern.matches(pathWithinApplication))) {
if (LOG.isTraceEnabled()) {
LOG.trace("Not tracing request " + request + " because it does not match any URL pattern: " + urlPatterns);
}
return false;
}
return true;
}
/**
* Get the public resource URL for the given URI string.
* <p>The URI string is expected to be a path and if it contains a query or
* fragment those will be preserved in the resulting public resource URL.
* @param uriString the URI string to transform
* @param exchange the current exchange
* @return the resolved public resource URL path, or empty if unresolved
*/
public final Mono<String> getForUriString(String uriString, ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
int queryIndex = getQueryIndex(uriString);
String lookupPath = uriString.substring(0, queryIndex);
String query = uriString.substring(queryIndex);
PathContainer parsedLookupPath = PathContainer.parsePath(lookupPath);
return resolveResourceUrl(exchange, parsedLookupPath).map(resolvedPath ->
request.getPath().contextPath().value() + resolvedPath + query);
}
@Override
public Mono<Resource> apply(ServerRequest request) {
PathContainer pathContainer = request.pathContainer();
if (!this.pattern.matches(pathContainer)) {
return Mono.empty();
}
pathContainer = this.pattern.extractPathWithinPattern(pathContainer);
String path = processPath(pathContainer.value());
if (path.contains("%")) {
path = StringUtils.uriDecode(path, StandardCharsets.UTF_8);
}
if (!StringUtils.hasLength(path) || isInvalidPath(path)) {
return Mono.empty();
}
try {
Resource resource = this.location.createRelative(path);
if (resource.exists() && resource.isReadable() && isResourceUnderLocation(resource)) {
return Mono.just(resource);
}
else {
return Mono.empty();
}
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
@Override
public boolean test(ServerRequest request) {
PathContainer pathContainer = request.pathContainer();
PathPattern.PathMatchInfo info = this.pattern.matchAndExtract(pathContainer);
traceMatch("Pattern", this.pattern.getPatternString(), request.path(), info != null);
if (info != null) {
mergeAttributes(request, info.getUriVariables(), this.pattern);
return true;
}
else {
return false;
}
}
@Override
public Mono<Object> getHandlerInternal(ServerWebExchange exchange) {
PathContainer lookupPath = exchange.getRequest().getPath().pathWithinApplication();
Object handler;
try {
handler = lookupHandler(lookupPath, exchange);
}
catch (Exception ex) {
return Mono.error(ex);
}
return Mono.justOrEmpty(handler);
}
/**
* Find the patterns matching the given lookup path. Invoking this method should
* yield results equivalent to those of calling
* {@link #getMatchingCondition(ServerWebExchange)}.
* This method is provided as an alternative to be used if no request is available
* (e.g. introspection, tooling, etc).
* @param exchange the current exchange
* @return a sorted set of matching patterns sorted with the closest match first
*/
private SortedSet<PathPattern> getMatchingPatterns(ServerWebExchange exchange) {
PathContainer lookupPath = exchange.getRequest().getPath().pathWithinApplication();
TreeSet<PathPattern> pathPatterns = new TreeSet<>();
for (PathPattern pattern : this.patterns) {
if (pattern.matches(lookupPath)) {
pathPatterns.add(pattern);
}
}
return pathPatterns;
}
/**
* Expose URI template variables, matrix variables, and producible media types in the request.
* @see HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
* @see HandlerMapping#MATRIX_VARIABLES_ATTRIBUTE
* @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
*/
@Override
protected void handleMatch(RequestMappingInfo info, HandlerMethod handlerMethod,
ServerWebExchange exchange) {
super.handleMatch(info, handlerMethod, exchange);
PathContainer lookupPath = exchange.getRequest().getPath().pathWithinApplication();
PathPattern bestPattern;
Map<String, String> uriVariables;
Map<String, MultiValueMap<String, String>> matrixVariables;
Set<PathPattern> patterns = info.getPatternsCondition().getPatterns();
if (patterns.isEmpty()) {
bestPattern = getPathPatternParser().parse(lookupPath.value());
uriVariables = Collections.emptyMap();
matrixVariables = Collections.emptyMap();
}
else {
bestPattern = patterns.iterator().next();
PathPattern.PathMatchInfo result = bestPattern.matchAndExtract(lookupPath);
Assert.notNull(result, () ->
"Expected bestPattern: " + bestPattern + " to match lookupPath " + lookupPath);
uriVariables = result.getUriVariables();
matrixVariables = result.getMatrixVariables();
}
exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handlerMethod);
exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables);
exchange.getAttributes().put(MATRIX_VARIABLES_ATTRIBUTE, matrixVariables);
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
exchange.getAttributes().put(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
@Test
public void mapPathToLocation() throws Exception {
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
exchange.getAttributes().put(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE,
PathContainer.parsePath("/testStylesheet.css"));
ResourceWebHandler handler = getHandler("/resources/**");
handler.handle(exchange).block(Duration.ofSeconds(5));
StepVerifier.create(exchange.getResponse().getBody())
.consumeNextWith(buf -> assertEquals("test stylesheet content",
DataBufferTestUtils.dumpString(buf, StandardCharsets.UTF_8)))
.expectComplete()
.verify();
}
@Override
public Optional<Resource> apply(ServerRequest request) {
PathContainer pathContainer = request.pathContainer();
if (!this.pattern.matches(pathContainer)) {
return Optional.empty();
}
pathContainer = this.pattern.extractPathWithinPattern(pathContainer);
String path = processPath(pathContainer.value());
if (path.contains("%")) {
path = StringUtils.uriDecode(path, StandardCharsets.UTF_8);
}
if (!StringUtils.hasLength(path) || isInvalidPath(path)) {
return Optional.empty();
}
try {
Resource resource = this.location.createRelative(path);
if (resource.exists() && resource.isReadable() && isResourceUnderLocation(resource)) {
return Optional.of(resource);
}
else {
return Optional.empty();
}
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
@Override
public boolean test(ServerRequest request) {
PathContainer pathContainer = request.pathContainer();
PathPattern.PathMatchInfo info = this.pattern.matchAndExtract(pathContainer);
traceMatch("Pattern", this.pattern.getPatternString(), request.path(), info != null);
if (info != null) {
mergeAttributes(request, info.getUriVariables(), this.pattern);
return true;
}
else {
return false;
}
}
@Override
@Nullable
public CorsConfiguration getCorsConfiguration(ServerWebExchange exchange) {
PathContainer lookupPath = exchange.getRequest().getPath().pathWithinApplication();
return this.corsConfigurations.entrySet().stream()
.filter(entry -> entry.getKey().matches(lookupPath))
.map(Map.Entry::getValue)
.findFirst()
.orElse(null);
}
/**
* Match the beginning of the given path and return the remaining portion
* not covered by this pattern. This is useful for matching nested routes
* where the path is matched incrementally at each level.
* @param pathContainer the candidate path to attempt to match against
* @return info object with the match result or {@code null} for no match
*/
@Nullable
public PathRemainingMatchInfo matchStartOfPath(PathContainer pathContainer) {
if (this.head == null) {
return new PathRemainingMatchInfo(pathContainer);
}
else if (!hasLength(pathContainer)) {
return null;
}
MatchingContext matchingContext = new MatchingContext(pathContainer, true);
matchingContext.setMatchAllowExtraPath();
boolean matches = this.head.matches(0, matchingContext);
if (!matches) {
return null;
}
else {
PathRemainingMatchInfo info;
if (matchingContext.remainingPathIndex == pathContainer.elements().size()) {
info = new PathRemainingMatchInfo(EMPTY_PATH, matchingContext.getPathMatchResult());
}
else {
info = new PathRemainingMatchInfo(pathContainer.subPath(matchingContext.remainingPathIndex),
matchingContext.getPathMatchResult());
}
return info;
}
}
/**
* Return the decoded value of the specified element.
* @param pathIndex path element index
* @return the decoded value
*/
String pathElementValue(int pathIndex) {
Element element = (pathIndex < this.pathLength) ? this.pathElements.get(pathIndex) : null;
if (element instanceof PathContainer.PathSegment) {
return ((PathContainer.PathSegment)element).valueToMatch();
}
return "";
}
private void checkMatches(String uriTemplate, String path) {
PathPatternParser parser = new PathPatternParser();
parser.setMatchOptionalTrailingSeparator(true);
PathPattern p = parser.parse(uriTemplate);
PathContainer pc = toPathContainer(path);
assertTrue(p.matches(pc));
}
/**
* Get the public resource URL for the given URI string.
* <p>The URI string is expected to be a path and if it contains a query or
* fragment those will be preserved in the resulting public resource URL.
* @param uriString the URI string to transform
* @param exchange the current exchange
* @return the resolved public resource URL path, or empty if unresolved
*/
public final Mono<String> getForUriString(String uriString, ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
int queryIndex = getQueryIndex(uriString);
String lookupPath = uriString.substring(0, queryIndex);
String query = uriString.substring(queryIndex);
PathContainer parsedLookupPath = PathContainer.parsePath(lookupPath);
return resolveResourceUrl(exchange, parsedLookupPath).map(resolvedPath ->
request.getPath().contextPath().value() + resolvedPath + query);
}