All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2013 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.mvc.method.annotation;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.util.StringValueResolver;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.condition.AbstractRequestCondition;
import org.springframework.web.servlet.mvc.condition.CompositeRequestCondition;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;

/**
 * Creates {@link RequestMappingInfo} instances from type and method-level
 * {@link RequestMapping @RequestMapping} annotations in
 * {@link Controller @Controller} classes.
 *
 * @author Arjen Poutsma
 * @author Rossen Stoyanchev
 * @since 3.1
 */
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
		implements EmbeddedValueResolverAware {

	private boolean useSuffixPatternMatch = true;

	private boolean useRegisteredSuffixPatternMatch = false;

	private boolean useTrailingSlashMatch = true;

	private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();

	private final List fileExtensions = new ArrayList();

	private StringValueResolver embeddedValueResolver;


	/**
	 * Whether to use suffix pattern match (".*") when matching patterns to
	 * requests. If enabled a method mapped to "/users" also matches to "/users.*".
	 * 

The default value is {@code true}. *

Also see {@link #setUseRegisteredSuffixPatternMatch(boolean)} for * more fine-grained control over specific suffixes to allow. */ public void setUseSuffixPatternMatch(boolean useSuffixPatternMatch) { this.useSuffixPatternMatch = useSuffixPatternMatch; } /** * Whether to use suffix pattern match for registered file extensions only * when matching patterns to requests. * *

If enabled, a controller method mapped to "/users" also matches to * "/users.json" assuming ".json" is a file extension registered with the * provided {@link #setContentNegotiationManager(ContentNegotiationManager) * contentNegotiationManager}. This can be useful for allowing only specific * URL extensions to be used as well as in cases where a "." in the URL path * can lead to ambiguous interpretation of path variable content, (e.g. given * "/users/{user}" and incoming URLs such as "/users/john.j.joe" and * "/users/john.j.joe.json"). * *

If enabled, this flag also enables * {@link #setUseSuffixPatternMatch(boolean) useSuffixPatternMatch}. The * default value is {@code false}. */ public void setUseRegisteredSuffixPatternMatch(boolean useRegisteredSuffixPatternMatch) { this.useRegisteredSuffixPatternMatch = useRegisteredSuffixPatternMatch; this.useSuffixPatternMatch = useRegisteredSuffixPatternMatch ? true : this.useSuffixPatternMatch; } /** * Whether to match to URLs irrespective of the presence of a trailing slash. * If enabled a method mapped to "/users" also matches to "/users/". *

The default value is {@code true}. */ public void setUseTrailingSlashMatch(boolean useTrailingSlashMatch) { this.useTrailingSlashMatch = useTrailingSlashMatch; } @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.embeddedValueResolver = resolver; } /** * Set the {@link ContentNegotiationManager} to use to determine requested media types. * If not set, the default constructor is used. */ public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) { Assert.notNull(contentNegotiationManager); this.contentNegotiationManager = contentNegotiationManager; } /** * Whether to use suffix pattern matching. */ public boolean useSuffixPatternMatch() { return this.useSuffixPatternMatch; } /** * Whether to use registered suffixes for pattern matching. */ public boolean useRegisteredSuffixPatternMatch() { return useRegisteredSuffixPatternMatch; } /** * Whether to match to URLs irrespective of the presence of a trailing slash. */ public boolean useTrailingSlashMatch() { return this.useTrailingSlashMatch; } /** * Return the configured {@link ContentNegotiationManager}. */ public ContentNegotiationManager getContentNegotiationManager() { return this.contentNegotiationManager; } /** * Return the file extensions to use for suffix pattern matching. */ public List getFileExtensions() { return this.fileExtensions; } @Override public void afterPropertiesSet() { if (this.useRegisteredSuffixPatternMatch) { this.fileExtensions.addAll(contentNegotiationManager.getAllFileExtensions()); } super.afterPropertiesSet(); } /** * {@inheritDoc} * Expects a handler to have a type-level @{@link Controller} annotation. */ @Override protected boolean isHandler(Class beanType) { return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) || (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null)); } /** * Uses method and type-level @{@link RequestMapping} annotations to create * the RequestMappingInfo. * * @return the created RequestMappingInfo, or {@code null} if the method * does not have a {@code @RequestMapping} annotation. * * @see #getCustomMethodCondition(Method) * @see #getCustomTypeCondition(Class) */ @Override protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) { RequestMappingInfo info = null; RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class); if (methodAnnotation != null) { RequestCondition methodCondition = getCustomMethodCondition(method); info = createRequestMappingInfo(methodAnnotation, methodCondition); RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class); if (typeAnnotation != null) { RequestCondition typeCondition = getCustomTypeCondition(handlerType); info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info); } } return info; } /** * Provide a custom type-level request condition. * The custom {@link RequestCondition} can be of any type so long as the * same condition type is returned from all calls to this method in order * to ensure custom request conditions can be combined and compared. * *

Consider extending {@link AbstractRequestCondition} for custom * condition types and using {@link CompositeRequestCondition} to provide * multiple custom conditions. * * @param handlerType the handler type for which to create the condition * @return the condition, or {@code null} */ protected RequestCondition getCustomTypeCondition(Class handlerType) { return null; } /** * Provide a custom method-level request condition. * The custom {@link RequestCondition} can be of any type so long as the * same condition type is returned from all calls to this method in order * to ensure custom request conditions can be combined and compared. * *

Consider extending {@link AbstractRequestCondition} for custom * condition types and using {@link CompositeRequestCondition} to provide * multiple custom conditions. * * @param method the handler method for which to create the condition * @return the condition, or {@code null} */ protected RequestCondition getCustomMethodCondition(Method method) { return null; } /** * Created a RequestMappingInfo from a RequestMapping annotation. */ protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition customCondition) { String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value()); return new RequestMappingInfo( new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(), this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions), new RequestMethodsRequestCondition(annotation.method()), new ParamsRequestCondition(annotation.params()), new HeadersRequestCondition(annotation.headers()), new ConsumesRequestCondition(annotation.consumes(), annotation.headers()), new ProducesRequestCondition(annotation.produces(), annotation.headers(), getContentNegotiationManager()), customCondition); } /** * Resolve placeholder values in the given array of patterns. * @return a new array with updated patterns */ protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) { if (this.embeddedValueResolver == null) { return patterns; } else { String[] resolvedPatterns = new String[patterns.length]; for (int i=0; i < patterns.length; i++) { resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]); } return resolvedPatterns; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy