Pattern suffix issue in AnnotationMethodHandlerAdapter
Issue: SPR-9333
Backport-Issue: SPR-9419
Backport-Commit: cf5d55173b
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -443,7 +443,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
|
|||||||
/**
|
/**
|
||||||
* This method always returns -1 since an annotated controller can have many methods,
|
* This method always returns -1 since an annotated controller can have many methods,
|
||||||
* each requiring separate lastModified calculations. Instead, an
|
* each requiring separate lastModified calculations. Instead, an
|
||||||
* @{@link RequestMapping}-annotated method can calculate the lastModified value, call
|
* {@link RequestMapping}-annotated method can calculate the lastModified value, call
|
||||||
* {@link org.springframework.web.context.request.WebRequest#checkNotModified(long)}
|
* {@link org.springframework.web.context.request.WebRequest#checkNotModified(long)}
|
||||||
* to check it, and return {@code null} if that returns {@code true}.
|
* to check it, and return {@code null} if that returns {@code true}.
|
||||||
* @see org.springframework.web.context.request.WebRequest#checkNotModified(long)
|
* @see org.springframework.web.context.request.WebRequest#checkNotModified(long)
|
||||||
@@ -588,7 +588,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
|
|||||||
if (!typeLevelPattern.startsWith("/")) {
|
if (!typeLevelPattern.startsWith("/")) {
|
||||||
typeLevelPattern = "/" + typeLevelPattern;
|
typeLevelPattern = "/" + typeLevelPattern;
|
||||||
}
|
}
|
||||||
if (getMatchingPattern(typeLevelPattern, lookupPath) != null) {
|
boolean useSuffixPattern = useSuffixPattern(request);
|
||||||
|
if (getMatchingPattern(typeLevelPattern, lookupPath, useSuffixPattern) != null) {
|
||||||
if (mappingInfo.matches(request)) {
|
if (mappingInfo.matches(request)) {
|
||||||
match = true;
|
match = true;
|
||||||
mappingInfo.addMatchedPattern(typeLevelPattern);
|
mappingInfo.addMatchedPattern(typeLevelPattern);
|
||||||
@@ -675,6 +676,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
|
|||||||
return (Boolean) request.getAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING);
|
return (Boolean) request.getAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean useSuffixPattern(HttpServletRequest request) {
|
||||||
|
Object value = request.getAttribute(DefaultAnnotationHandlerMapping.USE_DEFAULT_SUFFIX_PATTERN);
|
||||||
|
return (value != null) ? (Boolean) value : Boolean.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines the combined pattern for the given methodLevelPattern and path.
|
* Determines the combined pattern for the given methodLevelPattern and path.
|
||||||
* <p>Uses the following algorithm:
|
* <p>Uses the following algorithm:
|
||||||
@@ -687,6 +693,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
|
|||||||
* </ol>
|
* </ol>
|
||||||
*/
|
*/
|
||||||
private String getCombinedPattern(String methodLevelPattern, String lookupPath, HttpServletRequest request) {
|
private String getCombinedPattern(String methodLevelPattern, String lookupPath, HttpServletRequest request) {
|
||||||
|
boolean useSuffixPattern = useSuffixPattern(request);
|
||||||
if (useTypeLevelMapping(request)) {
|
if (useTypeLevelMapping(request)) {
|
||||||
String[] typeLevelPatterns = getTypeLevelMapping().value();
|
String[] typeLevelPatterns = getTypeLevelMapping().value();
|
||||||
for (String typeLevelPattern : typeLevelPatterns) {
|
for (String typeLevelPattern : typeLevelPatterns) {
|
||||||
@@ -694,7 +701,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
|
|||||||
typeLevelPattern = "/" + typeLevelPattern;
|
typeLevelPattern = "/" + typeLevelPattern;
|
||||||
}
|
}
|
||||||
String combinedPattern = pathMatcher.combine(typeLevelPattern, methodLevelPattern);
|
String combinedPattern = pathMatcher.combine(typeLevelPattern, methodLevelPattern);
|
||||||
String matchingPattern = getMatchingPattern(combinedPattern, lookupPath);
|
String matchingPattern = getMatchingPattern(combinedPattern, lookupPath, useSuffixPattern);
|
||||||
if (matchingPattern != null) {
|
if (matchingPattern != null) {
|
||||||
return matchingPattern;
|
return matchingPattern;
|
||||||
}
|
}
|
||||||
@@ -704,20 +711,20 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
|
|||||||
String bestMatchingPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
String bestMatchingPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
||||||
if (StringUtils.hasText(bestMatchingPattern) && bestMatchingPattern.endsWith("*")) {
|
if (StringUtils.hasText(bestMatchingPattern) && bestMatchingPattern.endsWith("*")) {
|
||||||
String combinedPattern = pathMatcher.combine(bestMatchingPattern, methodLevelPattern);
|
String combinedPattern = pathMatcher.combine(bestMatchingPattern, methodLevelPattern);
|
||||||
String matchingPattern = getMatchingPattern(combinedPattern, lookupPath);
|
String matchingPattern = getMatchingPattern(combinedPattern, lookupPath, useSuffixPattern);
|
||||||
if (matchingPattern != null && !matchingPattern.equals(bestMatchingPattern)) {
|
if (matchingPattern != null && !matchingPattern.equals(bestMatchingPattern)) {
|
||||||
return matchingPattern;
|
return matchingPattern;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getMatchingPattern(methodLevelPattern, lookupPath);
|
return getMatchingPattern(methodLevelPattern, lookupPath, useSuffixPattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMatchingPattern(String pattern, String lookupPath) {
|
private String getMatchingPattern(String pattern, String lookupPath, boolean useSuffixPattern) {
|
||||||
if (pattern.equals(lookupPath)) {
|
if (pattern.equals(lookupPath)) {
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
boolean hasSuffix = pattern.indexOf('.') != -1;
|
boolean hasSuffix = pattern.indexOf('.') != -1;
|
||||||
if (!hasSuffix) {
|
if (useSuffixPattern && !hasSuffix) {
|
||||||
String patternWithSuffix = pattern + ".*";
|
String patternWithSuffix = pattern + ".*";
|
||||||
if (pathMatcher.match(patternWithSuffix, lookupPath)) {
|
if (pathMatcher.match(patternWithSuffix, lookupPath)) {
|
||||||
return patternWithSuffix;
|
return patternWithSuffix;
|
||||||
@@ -727,7 +734,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
|
|||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
boolean endsWithSlash = pattern.endsWith("/");
|
boolean endsWithSlash = pattern.endsWith("/");
|
||||||
if (!endsWithSlash) {
|
if (useSuffixPattern && !endsWithSlash) {
|
||||||
String patternWithSlash = pattern + "/";
|
String patternWithSlash = pattern + "/";
|
||||||
if (pathMatcher.match(patternWithSlash, lookupPath)) {
|
if (pathMatcher.match(patternWithSlash, lookupPath)) {
|
||||||
return patternWithSlash;
|
return patternWithSlash;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2010 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,6 +22,7 @@ import java.util.HashMap;
|
|||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
@@ -81,9 +82,11 @@ import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMappin
|
|||||||
*/
|
*/
|
||||||
public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping {
|
public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping {
|
||||||
|
|
||||||
|
static final String USE_DEFAULT_SUFFIX_PATTERN = DefaultAnnotationHandlerMapping.class.getName() + ".useDefaultSuffixPattern";
|
||||||
|
|
||||||
private boolean useDefaultSuffixPattern = true;
|
private boolean useDefaultSuffixPattern = true;
|
||||||
|
|
||||||
private final Map<Class, RequestMapping> cachedMappings = new HashMap<Class, RequestMapping>();
|
private final Map<Class<?>, RequestMapping> cachedMappings = new HashMap<Class<?>, RequestMapping>();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,6 +232,7 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler
|
|||||||
if (mapping != null) {
|
if (mapping != null) {
|
||||||
validateMapping(mapping, request);
|
validateMapping(mapping, request);
|
||||||
}
|
}
|
||||||
|
request.setAttribute(USE_DEFAULT_SUFFIX_PATTERN, this.useDefaultSuffixPattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -351,6 +351,31 @@ public class UriTemplateServletAnnotationControllerTests {
|
|||||||
assertEquals("foo-foo", response.getContentAsString());
|
assertEquals("foo-foo", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SPR-9333
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public void suppressDefaultSuffixPattern() throws Exception {
|
||||||
|
servlet = new DispatcherServlet() {
|
||||||
|
@Override
|
||||||
|
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)
|
||||||
|
throws BeansException {
|
||||||
|
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||||
|
wac.registerBeanDefinition("controller", new RootBeanDefinition(VariableNamesController.class));
|
||||||
|
RootBeanDefinition mappingDef = new RootBeanDefinition(DefaultAnnotationHandlerMapping.class);
|
||||||
|
mappingDef.getPropertyValues().add("useDefaultSuffixPattern", false);
|
||||||
|
wac.registerBeanDefinition("handlerMapping", mappingDef);
|
||||||
|
wac.refresh();
|
||||||
|
return wac;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
servlet.init(new MockServletConfig());
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test/jsmith@mail.com");
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
servlet.service(request, response);
|
||||||
|
assertEquals("foo-jsmith@mail.com", response.getContentAsString());
|
||||||
|
}
|
||||||
|
|
||||||
// SPR-6906
|
// SPR-6906
|
||||||
@Test
|
@Test
|
||||||
public void controllerClassName() throws Exception {
|
public void controllerClassName() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user