Pattern suffix issue in AnnotationMethodHandlerAdapter

Issue: SPR-9333
Backport-Issue: SPR-9419
Backport-Commit: cf5d55173b
This commit is contained in:
Rossen Stoyanchev
2012-05-16 12:13:03 -04:00
parent 9e370208d0
commit 8143d66e4c
4 changed files with 52 additions and 16 deletions

View File

@@ -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;

View File

@@ -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);
} }
/** /**

View File

@@ -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 {