如何在带有注释映射的Spring MVC中使用不区分大小写的URL

通过我的spring mvc web应用程序,我有注释的映射工作,但是,它们区分大小写。 我找不到让它们不区分大小写的方法。 (我很想在Spring MVC中实现这一点,而不是以某种方式重定向流量)

Spring 4.2将支持不区分大小写的路径匹配。 您可以按如下方式配置它:

@Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override public void configurePathMatch(PathMatchConfigurer configurer) { AntPathMatcher matcher = new AntPathMatcher(); matcher.setCaseSensitive(false); configurer.setPathMatcher(matcher); } } 

根据这个webpost,您需要在Spring MVC中添加HandlerMapping和HandlerAdapter 。 Mapping将请求映射到相应的控制器,适配器负责使用控制器执行请求。

因此,您需要覆盖映射器和适配器的PathMatcher 。

Ex(将使所有@Controllers不区分大小写):

新匹配:

 public class CaseInsenseticePathMatcher extends AntPathMatcher { @Override protected boolean doMatch(String pattern, String path, boolean fullMatch, Map uriTemplateVariables) { System.err.println(pattern + " -- " + path); return super.doMatch(pattern.toLowerCase(), path.toLowerCase(), fullMatch, uriTemplateVariables); } } 

applicationContext.xml中:

                    

添加了与< mvc:annotation-driven >相同的内容。 (感谢David Parks的链接)

在Spring 3.2+ / Spring Boot中,您现在可以使用简化的Java配置设置不区分大小写的URL匹配。

首先,您需要创建CaseInsensitivePathMatcher.groovy或Java类:

 import org.springframework.util.AntPathMatcher class CaseInsensitivePathMatcher extends AntPathMatcher{ @Override protected boolean doMatch(String pattern, String path, boolean fullMatch, Map uriTemplateVariables) { super.doMatch(pattern.toLowerCase(), path.toLowerCase(), fullMatch, uriTemplateVariables) } } 

接下来,要实现这一点,您应该有一个使用Springs @Configuration注释的类,它扩展了WebMvcConfigurerAdapter类,如下所示(请注意,我的代码包含在.groovy类中):

 @Configuration public class ApplicationConfig extends WebMvcConfigurerAdapter 

然后将以下两个方法添加到类中:

 /** * Creates a patchMatcher bean that matches case insensitively * @return PathMatcher */ @Bean public PathMatcher pathMatcher() { new CaseInsensitivePathMatcher() } /** * Overrides the configurePathMatch() method in WebMvcConfigurerAdapter * 
Allows us to set a custom path matcher, used by the MVC for @RequestMapping's * @param configurer */ @Override public void configurePathMatch(PathMatchConfigurer configurer) { configurer.pathMatcher = pathMatcher() } }

就是这样,您现在应该使用最少的配置来设置不区分大小写的URL


smat解决方案的 问题报告


在smat的解决方案中 ,有一个小的副作用(我会责怪spring-mvc)。

起初, AntPathMatcher.doMatch()似乎返回true / false,具体取决于requested-url和controller-method的请求映射字符串(这是唯一应该在这里完成的事情)。 但是,此方法也用于另一个目的(这不是在文档中编写的!)。 另一个目的是在controller-method中为@PathVariable收集相应的值。 这些值在Map uriTemplateVariables (最后一个参数)中收集。这些收集的值用于作为参数值传递给controller-method。

例如,我们有这样的控制器方法,

 @RequestMapping("/code/{userCode}") public String getCode(@PathVariable("userCode") String userCode) { System.out.println(userCode); } 

如果我们使用URL, /code/AbD访问,那么使用 AntPathMatcher.doMatch() 解决方案将收集Map uriTemplateVariables中的userCode->abd Map uriTemplateVariables作为userCode->abd 。 由于我们对路径字符串进行了较低的封装,因此收集的值也较低。 这个较低的userCode值传递给我们的控制器

但是,我很感谢smat的解决方案,到目前为止我没有任何其他问题。



通过smat解决方案解决了这个问题。 在扩展的AntPathMatcher类中没有下层AntPathMatcher路径或模式字符串,我强制我的扩展AntPathMatcher使用我的自定义AntPathStringMatcher 。 我的自定义AntPathStringMatcher在不改变实际字符串大小写的情况下进行大小写匹配。

在下面的解决方案代码中,大多数代码都是从原始类代码中复制的(由于私有访问,我想要自定义的代码被隐藏为子类)。

自定义AntPathMatcher,

 public class CaseInsensitivePathMatcher extends AntPathMatcher { private final Map stringMatcherCache = new ConcurrentHashMap(); /** * Actually match the given path against the given * pattern. * * @param pattern * the pattern to match against * @param path * the path String to test * @param fullMatch * whether a full pattern match is required (else a pattern match * as far as the given base path goes is sufficient) * @return true if the supplied path matched, * false if it didn't */ protected boolean doMatch(String pattern, String path, boolean fullMatch, Map uriTemplateVariables) { if (path.startsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR) != pattern.startsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR)) { return false; } String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, AntPathMatcher.DEFAULT_PATH_SEPARATOR); String[] pathDirs = StringUtils.tokenizeToStringArray(path, AntPathMatcher.DEFAULT_PATH_SEPARATOR); int pattIdxStart = 0; int pattIdxEnd = pattDirs.length - 1; int pathIdxStart = 0; int pathIdxEnd = pathDirs.length - 1; // Match all elements up to the first ** while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { String patDir = pattDirs[pattIdxStart]; if ("**".equals(patDir)) { break; } if (!matchStrings(patDir, pathDirs[pathIdxStart], uriTemplateVariables)) { return false; } pattIdxStart++; pathIdxStart++; } if (pathIdxStart > pathIdxEnd) { // Path is exhausted, only match if rest of pattern is * or **'s if (pattIdxStart > pattIdxEnd) { return (pattern.endsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR) ? path.endsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR) : !path .endsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR)); } if (!fullMatch) { return true; } if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR)) { return true; } for (int i = pattIdxStart; i <= pattIdxEnd; i++) { if (!pattDirs[i].equals("**")) { return false; } } return true; } else if (pattIdxStart > pattIdxEnd) { // String not exhausted, but pattern is. Failure. return false; } else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) { // Path start definitely matches due to "**" part in pattern. return true; } // up to last '**' while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { String patDir = pattDirs[pattIdxEnd]; if (patDir.equals("**")) { break; } if (!matchStrings(patDir, pathDirs[pathIdxEnd], uriTemplateVariables)) { return false; } pattIdxEnd--; pathIdxEnd--; } if (pathIdxStart > pathIdxEnd) { // String is exhausted for (int i = pattIdxStart; i <= pattIdxEnd; i++) { if (!pattDirs[i].equals("**")) { return false; } } return true; } while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) { int patIdxTmp = -1; for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) { if (pattDirs[i].equals("**")) { patIdxTmp = i; break; } } if (patIdxTmp == pattIdxStart + 1) { // '**/**' situation, so skip one pattIdxStart++; continue; } // Find the pattern between padIdxStart & padIdxTmp in str between // strIdxStart & strIdxEnd int patLength = (patIdxTmp - pattIdxStart - 1); int strLength = (pathIdxEnd - pathIdxStart + 1); int foundIdx = -1; strLoop: for (int i = 0; i <= strLength - patLength; i++) { for (int j = 0; j < patLength; j++) { String subPat = pattDirs[pattIdxStart + j + 1]; String subStr = pathDirs[pathIdxStart + i + j]; if (!matchStrings(subPat, subStr, uriTemplateVariables)) { continue strLoop; } } foundIdx = pathIdxStart + i; break; } if (foundIdx == -1) { return false; } pattIdxStart = patIdxTmp; pathIdxStart = foundIdx + patLength; } for (int i = pattIdxStart; i <= pattIdxEnd; i++) { if (!pattDirs[i].equals("**")) { return false; } } return true; } /** * Tests whether or not a string matches against a pattern. The pattern may * contain two special characters:
* '*' means zero or more characters
* '?' means one and only one character * * @param pattern * pattern to match against. Must not be null. * @param str * string which must be matched against the pattern. Must not be * null. * @return true if the string matches against the pattern, or * false otherwise. */ private boolean matchStrings(String pattern, String str, Map uriTemplateVariables) { CaseInsensitiveAntPathStringMatcher matcher = this.stringMatcherCache.get(pattern); if (matcher == null) { matcher = new CaseInsensitiveAntPathStringMatcher(pattern); this.stringMatcherCache.put(pattern, matcher); } return matcher.matchStrings(str, uriTemplateVariables); }

}

自定义AntPathStringMatcher,

 public class CaseInsensitiveAntPathStringMatcher { private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}"); private static final String DEFAULT_VARIABLE_PATTERN = "(.*)"; private final Pattern pattern; private final List variableNames = new LinkedList(); /** Construct a new instance of the AntPatchStringMatcher. */ CaseInsensitiveAntPathStringMatcher(String pattern) { this.pattern = createPattern(pattern); } private Pattern createPattern(String pattern) { StringBuilder patternBuilder = new StringBuilder(); Matcher m = GLOB_PATTERN.matcher(pattern); int end = 0; while (m.find()) { patternBuilder.append(quote(pattern, end, m.start())); String match = m.group(); if ("?".equals(match)) { patternBuilder.append('.'); } else if ("*".equals(match)) { patternBuilder.append(".*"); } else if (match.startsWith("{") && match.endsWith("}")) { int colonIdx = match.indexOf(':'); if (colonIdx == -1) { patternBuilder.append(DEFAULT_VARIABLE_PATTERN); variableNames.add(m.group(1)); } else { String variablePattern = match.substring(colonIdx + 1, match.length() - 1); patternBuilder.append('('); patternBuilder.append(variablePattern); patternBuilder.append(')'); String variableName = match.substring(1, colonIdx); variableNames.add(variableName); } } end = m.end(); } patternBuilder.append(quote(pattern, end, pattern.length())); return Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE); // this line is updated to create case-insensitive pattern object } private String quote(String s, int start, int end) { if (start == end) { return ""; } return Pattern.quote(s.substring(start, end)); } /** * Main entry point. * * @return true if the string matches against the pattern, or false otherwise. */ public boolean matchStrings(String str, Map uriTemplateVariables) { Matcher matcher = pattern.matcher(str); if (matcher.matches()) { if (uriTemplateVariables != null) { // SPR-8455 Assert.isTrue(variableNames.size() == matcher.groupCount(), "The number of capturing groups in the pattern segment " + pattern + " does not match the number of URI template variables it defines, which can occur if " + " capturing groups are used in a URI template regex. Use non-capturing groups instead."); for (int i = 1; i <= matcher.groupCount(); i++) { String name = this.variableNames.get(i - 1); String value = matcher.group(i); uriTemplateVariables.put(name, value); } } return true; } else { return false; } } 

Spring 4.2中的bean文件示例,这只是支持v4.2 +:

    ...     

好吧,我无法回答你的问题(我试过了,我想我可以搞清楚)。 但是看到你在2天内没有收到任何回复,至少有一些线索:

这个例子似乎表明它是可能的:

http://webcache.googleusercontent.com/search?q=cache:ELj-ZQ8G4z0J:www.springbyexample.org/examples/sdms-simple-spring-mvc-web-module.html+case+insensitive+requestmapping+spring&cd= 3 HL = EN&CT = clnk&客户=火狐-一个

它在Spring中引用了这个类

http://static.springsource.org/spring/docs/3.0.4.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/support/ControllerClassNameHandlerMapping.html

我的猜测(也就是猜测)是,您需要扩展并使用正确的参数实现单个bean,以使其不区分大小写。 看到:

http://rapid-web.tumblr.com/post/296916668/what-does-annotation-driven-do

最后一点,我注意到在其他地方读到它说所有路径都默认为小写,你确认/MyPath不是由@RequestMapping("/mypath")吗?

再一次,我能做的最好的事情就是思考。 也许它会让你走得更远,可以提出一个更具体的问题来引导你找到答案 – 这就是这些事情有时会起作用的方式。 祝你好运!