/*
 * Copyright 2002-2018 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
 *
 *      http://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.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.Ordered;
import org.springframework.lang.Nullable;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.PathMatcher;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.context.request.WebRequestInterceptor;
import org.springframework.web.context.support.WebApplicationObjectSupport;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.CorsProcessor;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.DefaultCorsProcessor;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.util.UrlPathHelper;

Abstract base class for HandlerMapping implementations. Supports ordering, a default handler, handler interceptors, including handler interceptors mapped by path patterns.

Note: This base class does not support exposure of the HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE. Support for this attribute is up to concrete subclasses, typically based on request URL mappings.

Author:Juergen Hoeller, Rossen Stoyanchev
See Also:
Since:07.04.2003
/** * Abstract base class for {@link org.springframework.web.servlet.HandlerMapping} * implementations. Supports ordering, a default handler, handler interceptors, * including handler interceptors mapped by path patterns. * * <p>Note: This base class does <i>not</i> support exposure of the * {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}. Support for this attribute * is up to concrete subclasses, typically based on request URL mappings. * * @author Juergen Hoeller * @author Rossen Stoyanchev * @since 07.04.2003 * @see #getHandlerInternal * @see #setDefaultHandler * @see #setAlwaysUseFullPath * @see #setUrlDecode * @see org.springframework.util.AntPathMatcher * @see #setInterceptors * @see org.springframework.web.servlet.HandlerInterceptor */
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware { @Nullable private Object defaultHandler; private UrlPathHelper urlPathHelper = new UrlPathHelper(); private PathMatcher pathMatcher = new AntPathMatcher(); private final List<Object> interceptors = new ArrayList<>(); private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>(); private CorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource(); private CorsProcessor corsProcessor = new DefaultCorsProcessor(); private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered @Nullable private String beanName;
Set the default handler for this handler mapping. This handler will be returned if no specific mapping was found.

Default is null, indicating no default handler.

/** * Set the default handler for this handler mapping. * This handler will be returned if no specific mapping was found. * <p>Default is {@code null}, indicating no default handler. */
public void setDefaultHandler(@Nullable Object defaultHandler) { this.defaultHandler = defaultHandler; }
Return the default handler for this handler mapping, or null if none.
/** * Return the default handler for this handler mapping, * or {@code null} if none. */
@Nullable public Object getDefaultHandler() { return this.defaultHandler; }
Shortcut to same property on underlying UrlPathHelper.
See Also:
/** * Shortcut to same property on underlying {@link #setUrlPathHelper UrlPathHelper}. * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath(boolean) */
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) { this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath); if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) { ((UrlBasedCorsConfigurationSource)this.corsConfigurationSource).setAlwaysUseFullPath(alwaysUseFullPath); } }
Shortcut to same property on underlying UrlPathHelper.
See Also:
/** * Shortcut to same property on underlying {@link #setUrlPathHelper UrlPathHelper}. * @see org.springframework.web.util.UrlPathHelper#setUrlDecode(boolean) */
public void setUrlDecode(boolean urlDecode) { this.urlPathHelper.setUrlDecode(urlDecode); if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) { ((UrlBasedCorsConfigurationSource)this.corsConfigurationSource).setUrlDecode(urlDecode); } }
Shortcut to same property on underlying UrlPathHelper.
See Also:
/** * Shortcut to same property on underlying {@link #setUrlPathHelper UrlPathHelper}. * @see org.springframework.web.util.UrlPathHelper#setRemoveSemicolonContent(boolean) */
public void setRemoveSemicolonContent(boolean removeSemicolonContent) { this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent); if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) { ((UrlBasedCorsConfigurationSource)this.corsConfigurationSource).setRemoveSemicolonContent(removeSemicolonContent); } }
Set the UrlPathHelper to use for resolution of lookup paths.

Use this to override the default UrlPathHelper with a custom subclass, or to share common UrlPathHelper settings across multiple HandlerMappings and MethodNameResolvers.

/** * Set the UrlPathHelper to use for resolution of lookup paths. * <p>Use this to override the default UrlPathHelper with a custom subclass, * or to share common UrlPathHelper settings across multiple HandlerMappings * and MethodNameResolvers. */
public void setUrlPathHelper(UrlPathHelper urlPathHelper) { Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); this.urlPathHelper = urlPathHelper; if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) { ((UrlBasedCorsConfigurationSource)this.corsConfigurationSource).setUrlPathHelper(urlPathHelper); } }
Return the UrlPathHelper implementation to use for resolution of lookup paths.
/** * Return the UrlPathHelper implementation to use for resolution of lookup paths. */
public UrlPathHelper getUrlPathHelper() { return this.urlPathHelper; }
Set the PathMatcher implementation to use for matching URL paths against registered URL patterns. Default is AntPathMatcher.
See Also:
  • AntPathMatcher
/** * Set the PathMatcher implementation to use for matching URL paths * against registered URL patterns. Default is AntPathMatcher. * @see org.springframework.util.AntPathMatcher */
public void setPathMatcher(PathMatcher pathMatcher) { Assert.notNull(pathMatcher, "PathMatcher must not be null"); this.pathMatcher = pathMatcher; if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) { ((UrlBasedCorsConfigurationSource)this.corsConfigurationSource).setPathMatcher(pathMatcher); } }
Return the PathMatcher implementation to use for matching URL paths against registered URL patterns.
/** * Return the PathMatcher implementation to use for matching URL paths * against registered URL patterns. */
public PathMatcher getPathMatcher() { return this.pathMatcher; }
Set the interceptors to apply for all handlers mapped by this handler mapping.

Supported interceptor types are HandlerInterceptor, WebRequestInterceptor, and MappedInterceptor. Mapped interceptors apply only to request URLs that match its path patterns. Mapped interceptor beans are also detected by type during initialization.

Params:
  • interceptors – array of handler interceptors
See Also:
/** * Set the interceptors to apply for all handlers mapped by this handler mapping. * <p>Supported interceptor types are HandlerInterceptor, WebRequestInterceptor, and MappedInterceptor. * Mapped interceptors apply only to request URLs that match its path patterns. * Mapped interceptor beans are also detected by type during initialization. * @param interceptors array of handler interceptors * @see #adaptInterceptor * @see org.springframework.web.servlet.HandlerInterceptor * @see org.springframework.web.context.request.WebRequestInterceptor */
public void setInterceptors(Object... interceptors) { this.interceptors.addAll(Arrays.asList(interceptors)); }
Set the "global" CORS configurations based on URL patterns. By default the first matching URL pattern is combined with the CORS configuration for the handler, if any.
See Also:
Since:4.2
/** * Set the "global" CORS configurations based on URL patterns. By default the first * matching URL pattern is combined with the CORS configuration for the handler, if any. * @since 4.2 * @see #setCorsConfigurationSource(CorsConfigurationSource) */
public void setCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations) { Assert.notNull(corsConfigurations, "corsConfigurations must not be null"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.setCorsConfigurations(corsConfigurations); source.setPathMatcher(this.pathMatcher); source.setUrlPathHelper(this.urlPathHelper); this.corsConfigurationSource = source; }
Set the "global" CORS configuration source. By default the first matching URL pattern is combined with the CORS configuration for the handler, if any.
See Also:
Since:5.1
/** * Set the "global" CORS configuration source. By default the first matching URL * pattern is combined with the CORS configuration for the handler, if any. * @since 5.1 * @see #setCorsConfigurations(Map) */
public void setCorsConfigurationSource(CorsConfigurationSource corsConfigurationSource) { Assert.notNull(corsConfigurationSource, "corsConfigurationSource must not be null"); this.corsConfigurationSource = corsConfigurationSource; }
Get the "global" CORS configurations.
Deprecated:as of 5.1 since it is now possible to set a CorsConfigurationSource which is not a UrlBasedCorsConfigurationSource. Expected to be removed in 5.2.
/** * Get the "global" CORS configurations. * @deprecated as of 5.1 since it is now possible to set a {@link CorsConfigurationSource} which is not a * {@link UrlBasedCorsConfigurationSource}. Expected to be removed in 5.2. */
@Deprecated public Map<String, CorsConfiguration> getCorsConfigurations() { if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) { return ((UrlBasedCorsConfigurationSource)this.corsConfigurationSource).getCorsConfigurations(); } else { throw new IllegalStateException("No CORS configurations available when the source " + "is not an UrlBasedCorsConfigurationSource"); } }
Configure a custom CorsProcessor to use to apply the matched CorsConfiguration for a request.

By default DefaultCorsProcessor is used.

Since:4.2
/** * Configure a custom {@link CorsProcessor} to use to apply the matched * {@link CorsConfiguration} for a request. * <p>By default {@link DefaultCorsProcessor} is used. * @since 4.2 */
public void setCorsProcessor(CorsProcessor corsProcessor) { Assert.notNull(corsProcessor, "CorsProcessor must not be null"); this.corsProcessor = corsProcessor; }
Return the configured CorsProcessor.
/** * Return the configured {@link CorsProcessor}. */
public CorsProcessor getCorsProcessor() { return this.corsProcessor; }
Specify the order value for this HandlerMapping bean.

The default value is Ordered.LOWEST_PRECEDENCE, meaning non-ordered.

See Also:
/** * Specify the order value for this HandlerMapping bean. * <p>The default value is {@code Ordered.LOWEST_PRECEDENCE}, meaning non-ordered. * @see org.springframework.core.Ordered#getOrder() */
public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } @Override public void setBeanName(String name) { this.beanName = name; } protected String formatMappingName() { return this.beanName != null ? "'" + this.beanName + "'" : "<unknown>"; }
Initializes the interceptors.
See Also:
/** * Initializes the interceptors. * @see #extendInterceptors(java.util.List) * @see #initInterceptors() */
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); }
Extension hook that subclasses can override to register additional interceptors, given the configured interceptors (see setInterceptors).

Will be invoked before initInterceptors() adapts the specified interceptors into HandlerInterceptor instances.

The default implementation is empty.

Params:
  • interceptors – the configured interceptor List (never null), allowing to add further interceptors before as well as after the existing interceptors
/** * Extension hook that subclasses can override to register additional interceptors, * given the configured interceptors (see {@link #setInterceptors}). * <p>Will be invoked before {@link #initInterceptors()} adapts the specified * interceptors into {@link HandlerInterceptor} instances. * <p>The default implementation is empty. * @param interceptors the configured interceptor List (never {@code null}), allowing * to add further interceptors before as well as after the existing interceptors */
protected void extendInterceptors(List<Object> interceptors) { }
Detect beans of type MappedInterceptor and add them to the list of mapped interceptors.

This is called in addition to any MappedInterceptors that may have been provided via setInterceptors, by default adding all beans of type MappedInterceptor from the current context and its ancestors. Subclasses can override and refine this policy.

Params:
/** * Detect beans of type {@link MappedInterceptor} and add them to the list of mapped interceptors. * <p>This is called in addition to any {@link MappedInterceptor MappedInterceptors} that may have been provided * via {@link #setInterceptors}, by default adding all beans of type {@link MappedInterceptor} * from the current context and its ancestors. Subclasses can override and refine this policy. * @param mappedInterceptors an empty list to add {@link MappedInterceptor} instances to */
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( obtainApplicationContext(), MappedInterceptor.class, true, false).values()); }
Initialize the specified interceptors, checking for MappedInterceptors and adapting HandlerInterceptors and HandlerInterceptors and WebRequestInterceptors if necessary.
See Also:
/** * Initialize the specified interceptors, checking for {@link MappedInterceptor MappedInterceptors} and * adapting {@link HandlerInterceptor}s and {@link WebRequestInterceptor HandlerInterceptor}s and * {@link WebRequestInterceptor}s if necessary. * @see #setInterceptors * @see #adaptInterceptor */
protected void initInterceptors() { if (!this.interceptors.isEmpty()) { for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); } this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } }
Adapt the given interceptor object to the HandlerInterceptor interface.

By default, the supported interceptor types are HandlerInterceptor and WebRequestInterceptor. Each given WebRequestInterceptor will be wrapped in a WebRequestHandlerInterceptorAdapter. Can be overridden in subclasses.

Params:
  • interceptor – the specified interceptor object
See Also:
Returns:the interceptor wrapped as HandlerInterceptor
/** * Adapt the given interceptor object to the {@link HandlerInterceptor} interface. * <p>By default, the supported interceptor types are {@link HandlerInterceptor} * and {@link WebRequestInterceptor}. Each given {@link WebRequestInterceptor} * will be wrapped in a {@link WebRequestHandlerInterceptorAdapter}. * Can be overridden in subclasses. * @param interceptor the specified interceptor object * @return the interceptor wrapped as HandlerInterceptor * @see org.springframework.web.servlet.HandlerInterceptor * @see org.springframework.web.context.request.WebRequestInterceptor * @see WebRequestHandlerInterceptorAdapter */
protected HandlerInterceptor adaptInterceptor(Object interceptor) { if (interceptor instanceof HandlerInterceptor) { return (HandlerInterceptor) interceptor; } else if (interceptor instanceof WebRequestInterceptor) { return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor); } else { throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName()); } }
Return the adapted interceptors as HandlerInterceptor array.
Returns:the array of HandlerInterceptors, or null if none
/** * Return the adapted interceptors as {@link HandlerInterceptor} array. * @return the array of {@link HandlerInterceptor HandlerInterceptors}, or {@code null} if none */
@Nullable protected final HandlerInterceptor[] getAdaptedInterceptors() { return (!this.adaptedInterceptors.isEmpty() ? this.adaptedInterceptors.toArray(new HandlerInterceptor[0]) : null); }
Return all configured MappedInterceptors as an array.
Returns:the array of MappedInterceptors, or null if none
/** * Return all configured {@link MappedInterceptor MappedInterceptors} as an array. * @return the array of {@link MappedInterceptor MappedInterceptors}, or {@code null} if none */
@Nullable protected final MappedInterceptor[] getMappedInterceptors() { List<MappedInterceptor> mappedInterceptors = new ArrayList<>(this.adaptedInterceptors.size()); for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { mappedInterceptors.add((MappedInterceptor) interceptor); } } return (!mappedInterceptors.isEmpty() ? mappedInterceptors.toArray(new MappedInterceptor[0]) : null); }
Look up a handler for the given request, falling back to the default handler if no specific one is found.
Params:
  • request – current HTTP request
See Also:
Returns:the corresponding handler instance, or the default handler
/** * Look up a handler for the given request, falling back to the default * handler if no specific one is found. * @param request current HTTP request * @return the corresponding handler instance, or the default handler * @see #getHandlerInternal */
@Override @Nullable public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) { logger.debug("Mapped to " + executionChain.getHandler()); } if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
Look up a handler for the given request, returning null if no specific one is found. This method is called by getHandler; a null return value will lead to the default handler, if one is set.

On CORS pre-flight requests this method should return a match not for the pre-flight request but for the expected actual request based on the URL path, the HTTP methods from the "Access-Control-Request-Method" header, and the headers from the "Access-Control-Request-Headers" header thus allowing the CORS configuration to be obtained via getCorsConfiguration(Object, HttpServletRequest),

Note: This method may also return a pre-built HandlerExecutionChain, combining a handler object with dynamically determined interceptors. Statically specified interceptors will get merged into such an existing chain.

Params:
  • request – current HTTP request
Throws:
Returns:the corresponding handler instance, or null if none found
/** * Look up a handler for the given request, returning {@code null} if no * specific one is found. This method is called by {@link #getHandler}; * a {@code null} return value will lead to the default handler, if one is set. * <p>On CORS pre-flight requests this method should return a match not for * the pre-flight request but for the expected actual request based on the URL * path, the HTTP methods from the "Access-Control-Request-Method" header, and * the headers from the "Access-Control-Request-Headers" header thus allowing * the CORS configuration to be obtained via {@link #getCorsConfiguration(Object, HttpServletRequest)}, * <p>Note: This method may also return a pre-built {@link HandlerExecutionChain}, * combining a handler object with dynamically determined interceptors. * Statically specified interceptors will get merged into such an existing chain. * @param request current HTTP request * @return the corresponding handler instance, or {@code null} if none found * @throws Exception if there is an internal error */
@Nullable protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
Build a HandlerExecutionChain for the given handler, including applicable interceptors.

The default implementation builds a standard HandlerExecutionChain with the given handler, the handler mapping's common interceptors, and any MappedInterceptors matching to the current request URL. Interceptors are added in the order they were registered. Subclasses may override this in order to extend/rearrange the list of interceptors.

NOTE: The passed-in handler object may be a raw handler or a pre-built HandlerExecutionChain. This method should handle those two cases explicitly, either building a new HandlerExecutionChain or extending the existing chain.

For simply adding an interceptor in a custom subclass, consider calling super.getHandlerExecutionChain(handler, request) and invoking HandlerExecutionChain.addInterceptor on the returned chain object.

Params:
  • handler – the resolved handler instance (never null)
  • request – current HTTP request
See Also:
Returns:the HandlerExecutionChain (never null)
/** * Build a {@link HandlerExecutionChain} for the given handler, including * applicable interceptors. * <p>The default implementation builds a standard {@link HandlerExecutionChain} * with the given handler, the handler mapping's common interceptors, and any * {@link MappedInterceptor MappedInterceptors} matching to the current request URL. Interceptors * are added in the order they were registered. Subclasses may override this * in order to extend/rearrange the list of interceptors. * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a * pre-built {@link HandlerExecutionChain}. This method should handle those * two cases explicitly, either building a new {@link HandlerExecutionChain} * or extending the existing chain. * <p>For simply adding an interceptor in a custom subclass, consider calling * {@code super.getHandlerExecutionChain(handler, request)} and invoking * {@link HandlerExecutionChain#addInterceptor} on the returned chain object. * @param handler the resolved handler instance (never {@code null}) * @param request current HTTP request * @return the HandlerExecutionChain (never {@code null}) * @see #getAdaptedInterceptors() */
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
Retrieve the CORS configuration for the given handler.
Params:
  • handler – the handler to check (never null).
  • request – the current request.
Returns:the CORS configuration for the handler, or null if none
Since:4.2
/** * Retrieve the CORS configuration for the given handler. * @param handler the handler to check (never {@code null}). * @param request the current request. * @return the CORS configuration for the handler, or {@code null} if none * @since 4.2 */
@Nullable protected CorsConfiguration getCorsConfiguration(Object handler, HttpServletRequest request) { Object resolvedHandler = handler; if (handler instanceof HandlerExecutionChain) { resolvedHandler = ((HandlerExecutionChain) handler).getHandler(); } if (resolvedHandler instanceof CorsConfigurationSource) { return ((CorsConfigurationSource) resolvedHandler).getCorsConfiguration(request); } return null; }
Update the HandlerExecutionChain for CORS-related handling.

For pre-flight requests, the default implementation replaces the selected handler with a simple HttpRequestHandler that invokes the configured setCorsProcessor.

For actual requests, the default implementation inserts a HandlerInterceptor that makes CORS-related checks and adds CORS headers.

Params:
  • request – the current request
  • chain – the handler chain
  • config – the applicable CORS configuration (possibly null)
Since:4.2
/** * Update the HandlerExecutionChain for CORS-related handling. * <p>For pre-flight requests, the default implementation replaces the selected * handler with a simple HttpRequestHandler that invokes the configured * {@link #setCorsProcessor}. * <p>For actual requests, the default implementation inserts a * HandlerInterceptor that makes CORS-related checks and adds CORS headers. * @param request the current request * @param chain the handler chain * @param config the applicable CORS configuration (possibly {@code null}) * @since 4.2 */
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request, HandlerExecutionChain chain, @Nullable CorsConfiguration config) { if (CorsUtils.isPreFlightRequest(request)) { HandlerInterceptor[] interceptors = chain.getInterceptors(); chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors); } else { chain.addInterceptor(new CorsInterceptor(config)); } return chain; } private class PreFlightHandler implements HttpRequestHandler, CorsConfigurationSource { @Nullable private final CorsConfiguration config; public PreFlightHandler(@Nullable CorsConfiguration config) { this.config = config; } @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { corsProcessor.processRequest(this.config, request, response); } @Override @Nullable public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { return this.config; } } private class CorsInterceptor extends HandlerInterceptorAdapter implements CorsConfigurationSource { @Nullable private final CorsConfiguration config; public CorsInterceptor(@Nullable CorsConfiguration config) { this.config = config; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return corsProcessor.processRequest(this.config, request, response); } @Override @Nullable public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { return this.config; } } }