/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet.handler;

import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.server.PathContainer;
import org.springframework.lang.Nullable;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.request.WebRequestInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;

Wraps a HandlerInterceptor and uses URL patterns to determine whether it applies to a given request.

Pattern matching can be done with PathMatcher or with parsed PathPattern. The syntax is largely the same with the latter being more tailored for web usage and more efficient. The choice is driven by the presence of a resolved String lookupPath or a parsed RequestPath which in turn depends on the HandlerMapping that matched the current request.

MappedInterceptor is supported by sub-classes of AbstractHandlerMethodMapping which detect beans of type MappedInterceptor and also check if interceptors directly registered with it are of this type.

Author:Keith Donald, Rossen Stoyanchev, Brian Clozel
Since:3.0
/** * Wraps a {@link HandlerInterceptor} and uses URL patterns to determine whether * it applies to a given request. * * <p>Pattern matching can be done with {@link PathMatcher} or with parsed * {@link PathPattern}. The syntax is largely the same with the latter being more * tailored for web usage and more efficient. The choice is driven by the * presence of a {@link UrlPathHelper#resolveAndCacheLookupPath resolved} * {@code String} lookupPath or a {@link ServletRequestPathUtils#parseAndCache * parsed} {@code RequestPath} which in turn depends on the * {@link HandlerMapping} that matched the current request. * * <p>{@code MappedInterceptor} is supported by sub-classes of * {@link org.springframework.web.servlet.handler.AbstractHandlerMethodMapping * AbstractHandlerMethodMapping} which detect beans of type * {@code MappedInterceptor} and also check if interceptors directly registered * with it are of this type. * * @author Keith Donald * @author Rossen Stoyanchev * @author Brian Clozel * @since 3.0 */
public final class MappedInterceptor implements HandlerInterceptor { private static PathMatcher defaultPathMatcher = new AntPathMatcher(); @Nullable private final PathPattern[] includePatterns; @Nullable private final PathPattern[] excludePatterns; private PathMatcher pathMatcher = defaultPathMatcher; private final HandlerInterceptor interceptor;
Create an instance with the given include and exclude patterns along with the target interceptor for the mappings.
Params:
  • includePatterns – patterns to which requests must match, or null to match all paths
  • excludePatterns – patterns to which requests must not match
  • interceptor – the target interceptor
  • parser – a parser to use to pre-parse patterns into PathPattern; when not provided, PathPatternParser.defaultInstance is used.
Since:5.3
/** * Create an instance with the given include and exclude patterns along with * the target interceptor for the mappings. * @param includePatterns patterns to which requests must match, or null to * match all paths * @param excludePatterns patterns to which requests must not match * @param interceptor the target interceptor * @param parser a parser to use to pre-parse patterns into {@link PathPattern}; * when not provided, {@link PathPatternParser#defaultInstance} is used. * @since 5.3 */
public MappedInterceptor(@Nullable String[] includePatterns, @Nullable String[] excludePatterns, HandlerInterceptor interceptor, @Nullable PathPatternParser parser) { this.includePatterns = initPatterns(includePatterns, parser); this.excludePatterns = initPatterns(excludePatterns, parser); this.interceptor = interceptor; } @Nullable private static PathPattern[] initPatterns( @Nullable String[] patterns, @Nullable PathPatternParser parser) { if (ObjectUtils.isEmpty(patterns)) { return null; } parser = (parser != null ? parser : PathPatternParser.defaultInstance); return Arrays.stream(patterns).map(parser::parse).toArray(PathPattern[]::new); } /** * Variant of * {@link #MappedInterceptor(String[], String[], HandlerInterceptor, PathPatternParser)} * with include patterns only. */ public MappedInterceptor(@Nullable String[] includePatterns, HandlerInterceptor interceptor) { this(includePatterns, null, interceptor); } /** * Variant of * {@link #MappedInterceptor(String[], String[], HandlerInterceptor, PathPatternParser)} * without a provided parser. */ public MappedInterceptor(@Nullable String[] includePatterns, @Nullable String[] excludePatterns, HandlerInterceptor interceptor) { this(includePatterns, excludePatterns, interceptor, null); } /** * Variant of * {@link #MappedInterceptor(String[], String[], HandlerInterceptor, PathPatternParser)} * with a {@link WebRequestInterceptor} as the target. */ public MappedInterceptor(@Nullable String[] includePatterns, WebRequestInterceptor interceptor) { this(includePatterns, null, interceptor); } /** * Variant of * {@link #MappedInterceptor(String[], String[], HandlerInterceptor, PathPatternParser)} * with a {@link WebRequestInterceptor} as the target. */ public MappedInterceptor(@Nullable String[] includePatterns, @Nullable String[] excludePatterns, WebRequestInterceptor interceptor) { this(includePatterns, excludePatterns, new WebRequestHandlerInterceptorAdapter(interceptor)); }
Return the patterns this interceptor is mapped to.
/** * Return the patterns this interceptor is mapped to. */
@Nullable public String[] getPathPatterns() { return (!ObjectUtils.isEmpty(this.includePatterns) ? Arrays.stream(this.includePatterns).map(PathPattern::getPatternString).toArray(String[]::new) : null); }
The target HandlerInterceptor to invoke in case of a match.
/** * The target {@link HandlerInterceptor} to invoke in case of a match. */
public HandlerInterceptor getInterceptor() { return this.interceptor; }
Configure the PathMatcher to use to match URL paths with against include and exclude patterns.

This is an advanced property that should be used only when a customized AntPathMatcher or a custom PathMatcher is required.

By default this is AntPathMatcher.

Note: Setting PathMatcher enforces use of String pattern matching even when a parsed RequestPath is available.

/** * Configure the PathMatcher to use to match URL paths with against include * and exclude patterns. * <p>This is an advanced property that should be used only when a * customized {@link AntPathMatcher} or a custom PathMatcher is required. * <p>By default this is {@link AntPathMatcher}. * <p><strong>Note:</strong> Setting {@code PathMatcher} enforces use of * String pattern matching even when a * {@link ServletRequestPathUtils#parseAndCache parsed} {@code RequestPath} * is available. */
public void setPathMatcher(PathMatcher pathMatcher) { this.pathMatcher = pathMatcher; }
The configured PathMatcher.
/** * The {@link #setPathMatcher(PathMatcher) configured} PathMatcher. */
public PathMatcher getPathMatcher() { return this.pathMatcher; }
Check whether this interceptor is mapped to the request.

The request mapping path is expected to have been resolved externally. See also class-level Javadoc.

Params:
  • request – the request to match to
Returns:true if the interceptor should be applied to the request
/** * Check whether this interceptor is mapped to the request. * <p>The request mapping path is expected to have been resolved externally. * See also class-level Javadoc. * @param request the request to match to * @return {@code true} if the interceptor should be applied to the request */
public boolean matches(HttpServletRequest request) { Object path = ServletRequestPathUtils.getCachedPath(request); if (this.pathMatcher != defaultPathMatcher) { path = path.toString(); } boolean isPathContainer = (path instanceof PathContainer); if (!ObjectUtils.isEmpty(this.excludePatterns)) { for (PathPattern pattern : this.excludePatterns) { if (matchPattern(path, isPathContainer, pattern)) { return false; } } } if (ObjectUtils.isEmpty(this.includePatterns)) { return true; } for (PathPattern pattern : this.includePatterns) { if (matchPattern(path, isPathContainer, pattern)) { return true; } } return false; } private boolean matchPattern(Object path, boolean isPathContainer, PathPattern pattern) { return (isPathContainer ? pattern.matches((PathContainer) path) : this.pathMatcher.match(pattern.getPatternString(), (String) path)); }
Determine a match for the given lookup path.
Params:
  • lookupPath – the current request path
  • pathMatcher – a path matcher for path pattern matching
Returns:true if the interceptor applies to the given request path
Deprecated:as of 5.3 in favor of matches(HttpServletRequest)
/** * Determine a match for the given lookup path. * @param lookupPath the current request path * @param pathMatcher a path matcher for path pattern matching * @return {@code true} if the interceptor applies to the given request path * @deprecated as of 5.3 in favor of {@link #matches(HttpServletRequest)} */
@Deprecated public boolean matches(String lookupPath, PathMatcher pathMatcher) { pathMatcher = (this.pathMatcher != defaultPathMatcher ? this.pathMatcher : pathMatcher); if (!ObjectUtils.isEmpty(this.excludePatterns)) { for (PathPattern pattern : this.excludePatterns) { if (pathMatcher.match(pattern.getPatternString(), lookupPath)) { return false; } } } if (ObjectUtils.isEmpty(this.includePatterns)) { return true; } for (PathPattern pattern : this.includePatterns) { if (pathMatcher.match(pattern.getPatternString(), lookupPath)) { return true; } } return false; } // HandlerInterceptor delegation @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return this.interceptor.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { this.interceptor.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { this.interceptor.afterCompletion(request, response, handler, ex); } }