diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java index 75ba08c540ff3b6259d3a5939165804e7cf25137..cc874c7145613894279660dd873b8048ec3510de 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java @@ -208,7 +208,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { */ protected void validateHandler(Object handler, HttpServletRequest request) throws Exception { } - + /** * Build a handler object for the given raw handler, exposing the actual * handler, the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}, as well as diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index 6a0ef788b8b3364ed6d924fedb884a7f59d75da5..20dd1d07448a81647f6833b6cc945a19d715fa20 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java @@ -588,7 +588,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator if (!typeLevelPattern.startsWith("/")) { typeLevelPattern = "/" + typeLevelPattern; } - if (getMatchingPattern(typeLevelPattern, lookupPath) != null) { + boolean useSuffixPattern = useSuffixPattern(request); + if (getMatchingPattern(typeLevelPattern, lookupPath, useSuffixPattern) != null) { if (mappingInfo.matches(request)) { match = true; mappingInfo.addMatchedPattern(typeLevelPattern); @@ -675,6 +676,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator 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. *

Uses the following algorithm: @@ -687,6 +693,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator * */ private String getCombinedPattern(String methodLevelPattern, String lookupPath, HttpServletRequest request) { + boolean useSuffixPattern = useSuffixPattern(request); if (useTypeLevelMapping(request)) { String[] typeLevelPatterns = getTypeLevelMapping().value(); for (String typeLevelPattern : typeLevelPatterns) { @@ -694,7 +701,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator typeLevelPattern = "/" + typeLevelPattern; } String combinedPattern = pathMatcher.combine(typeLevelPattern, methodLevelPattern); - String matchingPattern = getMatchingPattern(combinedPattern, lookupPath); + String matchingPattern = getMatchingPattern(combinedPattern, lookupPath, useSuffixPattern); if (matchingPattern != null) { return matchingPattern; } @@ -704,20 +711,20 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator String bestMatchingPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); if (StringUtils.hasText(bestMatchingPattern) && bestMatchingPattern.endsWith("*")) { String combinedPattern = pathMatcher.combine(bestMatchingPattern, methodLevelPattern); - String matchingPattern = getMatchingPattern(combinedPattern, lookupPath); + String matchingPattern = getMatchingPattern(combinedPattern, lookupPath, useSuffixPattern); if (matchingPattern != null && !matchingPattern.equals(bestMatchingPattern)) { 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)) { return pattern; } boolean hasSuffix = pattern.indexOf('.') != -1; - if (!hasSuffix) { + if (useSuffixPattern && !hasSuffix) { String patternWithSuffix = pattern + ".*"; if (pathMatcher.match(patternWithSuffix, lookupPath)) { return patternWithSuffix; @@ -727,7 +734,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator return pattern; } boolean endsWithSlash = pattern.endsWith("/"); - if (!endsWithSlash) { + if (useSuffixPattern && !endsWithSlash) { String patternWithSlash = pattern + "/"; if (pathMatcher.match(patternWithSlash, lookupPath)) { return patternWithSlash; @@ -1236,7 +1243,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator private int compareAcceptHeaders(RequestMappingInfo info1, RequestMappingInfo info2) { List requestAccepts = request.getHeaders().getAccept(); MediaType.sortByQualityValue(requestAccepts); - + List info1Accepts = getAcceptHeaderValue(info1); List info2Accepts = getAcceptHeaderValue(info2); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java index c4c3eb263fd3aa993ba891a95c56cdf7a434dcde..19a62b6f875f91227506909a088992f0a8668060 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java @@ -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"); * 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.Map; import java.util.Set; + import javax.servlet.http.HttpServletRequest; import org.springframework.context.ApplicationContext; @@ -81,9 +82,11 @@ import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMappin */ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping { + static final String USE_DEFAULT_SUFFIX_PATTERN = DefaultAnnotationHandlerMapping.class.getName() + ".useDefaultSuffixPattern"; + private boolean useDefaultSuffixPattern = true; - private final Map cachedMappings = new HashMap(); + private final Map, RequestMapping> cachedMappings = new HashMap, RequestMapping>(); /** @@ -229,6 +232,7 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler if (mapping != null) { validateMapping(mapping, request); } + request.setAttribute(USE_DEFAULT_SUFFIX_PATTERN, this.useDefaultSuffixPattern); } /** diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java index e5edf988d8e539b40fa53179b00ae3ab86ab10d3..2d371641a9f7fb636125506dda380c033c38b0d2 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -351,6 +351,31 @@ public class UriTemplateServletAnnotationControllerTests { 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 @Test public void controllerClassName() throws Exception { @@ -389,7 +414,7 @@ public class UriTemplateServletAnnotationControllerTests { @Test public void doIt() throws Exception { initServlet(Spr6978Controller.class); - + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/100"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response);