尝试在“文件名”部分创建具有多个点的REST-ful URL – Spring 3.0 MVC

我正在使用带有注释驱动控制器的Spring MVC(3.0)。 我想为资源创建REST-ful URL,并且不能要求(但仍然可选地允许)URL末尾的文件扩展名(但如果没有扩展名则假设HTML内容类型)。 只要文件名部分没有点(句点/句号),这就可以与Spring MVC一起开箱即用。

但是,我的某些url需要名称中带有点的标识符。 像这样:

http://company.com/widgets/123.456.789.500 

在这种情况下,Spring会查找扩展名为.500的内容类型,并且找不到任何错误。 我可以使用解决方法,比如在最后添加.html ,编码标识符或添加尾部斜杠。 我对这些不满意,但可能会加入.html

我没有成功地寻找一种覆盖Spring中默认文件扩展名检测的方法。

是否可以为给定的控制器方法或URL模式等自定义或禁用文件扩展名检测?

当涉及到URL中的点时, @PathVariable模式匹配有点抽搐(参见SPR-5778 )。 通过将DefaultAnnotationHandlerMapping上的useDefaultSuffixPattern属性设置为false ,您可以减少麻烦(但更挑剔),并更好地控制点重URL。

如果您尚未在上下文中显式声明DefaultAnnotationHandlerMapping (并且大多数人都没有,因为它是为您隐式声明的),那么您可以显式添加它,并设置该属性。

可能,这是一个丑陋的黑客,我只是想探索Spring @MVC的可扩展性。 这是一个自定义的PathMatcher 。 它使用模式中的$作为结束标记 – 如果模式以它结束,标记被移除并且模式由默认匹配器匹配,但如果模式中间有$ (例如...$.* ),则这样的模式不匹配。

 public class CustomPathMatcher implements PathMatcher { private PathMatcher target; public CustomPathMatcher() { target = new AntPathMatcher(); } public String combine(String pattern1, String pattern2) { return target.combine(pattern1, pattern2); } public String extractPathWithinPattern(String pattern, String path) { if (isEncoded(pattern)) { pattern = resolvePattern(pattern); if (pattern == null) return ""; } return target.extractPathWithinPattern(pattern, path); } public Map extractUriTemplateVariables(String pattern, String path) { if (isEncoded(pattern)) { pattern = resolvePattern(pattern); if (pattern == null) return Collections.emptyMap(); } return target.extractUriTemplateVariables(pattern, path); } public Comparator getPatternComparator(String pattern) { final Comparator targetComparator = target.getPatternComparator(pattern); return new Comparator() { public int compare(String o1, String o2) { if (isEncoded(o1)) { if (isEncoded(o2)) { return 0; } else { return -1; } } else if (isEncoded(o2)) { return 1; } return targetComparator.compare(o1, o2); } }; } public boolean isPattern(String pattern) { if (isEncoded(pattern)) { pattern = resolvePattern(pattern); if (pattern == null) return true; } return target.isPattern(pattern); } public boolean match(String pattern, String path) { if (isEncoded(pattern)) { pattern = resolvePattern(pattern); if (pattern == null) return false; } return target.match(pattern, path); } public boolean matchStart(String pattern, String path) { if (isEncoded(pattern)) { pattern = resolvePattern(pattern); if (pattern == null) return false; } return target.match(pattern, path); } private boolean isEncoded(String pattern) { return pattern != null && pattern.contains("$"); } private String resolvePattern(String pattern) { int i = pattern.indexOf('$'); if (i < 0) return pattern; else if (i == pattern.length() - 1) { return pattern.substring(0, i); } else { String tail = pattern.substring(i + 1); if (tail.startsWith(".")) return null; else return pattern.substring(0, i) + tail; } } } 

配置:

        

和用法(给定“/hello/1.2.3”, value “1.2.3”):

 @RequestMapping(value = "/hello/{value}$", method = RequestMethod.GET) public String hello(@PathVariable("value") String value, ModelMap model) 

编辑::现在不打破“尾随斜线无关紧要”的规则

  

@Controller public class MyController { @RequestMapping(value="/widgets/{preDot}.{postDot}") public void getResource(@PathVariable String preDot, @PathVariable String postDot) { String fullPath = preDot + "." + postDot; //... } }

//上面的代码应该匹配/widgets/111.222.333.444

Spring 3.2已更改,并建议您在RequestMappingHandlerMapping bean上设置属性,显式(如果不使用mvc命名空间) 或使用BeanPostProcessor (如下所示)(您需要扫描或实例化它):

 @Component public class IncludeExtensionsInRequestParamPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof RequestMappingHandlerMapping) { RequestMappingHandlerMapping mapping = (RequestMappingHandlerMapping)bean; mapping.setUseRegisteredSuffixPatternMatch(false); mapping.setUseSuffixPatternMatch(false); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; } } 

您也可以在您的@RequestMapping中附加:.* ,例如"/{documentPath:.*}" :.* "/{documentPath:.*}" (参见JIRA评论)

我有同样的问题,我也通过自定义PathMatcher解决了它。 我的解决方案比axtavt提出的解决方案稍微简单一些。 我的PathMatcher还有一个私有的最终AntPathMatcher目标,并且除了match()方法之外,它将所有对它的调用都保持不变。

 @Override public boolean match(String pattern, String path) { return pattern.endsWith(".*") ? false : target.match(pattern, path); } 

这是有效的,因为Spring试图通过在结尾添加“。 ”来匹配控制器 例如,使用路径映射“/ widgets / {id}”和URL“/widgets/1.2.3.4”,Spring尝试首先匹配“/ widgets / {id}。 ”,之后“/ widgets / {id}” 。 第一个匹配,但它只留下id的“1.2.3”。

我的PatchMatcher专门拒绝以“。*”结尾的模式,因此第一次尝试失败而第二次尝试失败。

如果您正在使用ContentNegotiatingViewResolver,您仍然可以使用请求参数“format”在URL中指定内容类型(如果favorParameter设置为true)。

-jarppe

JFY:在Spring 4中,这个问题通过以下方式解决:WebMvcConfigurerAdapter。

 @Configuration class MvcConfiguration extends WebMvcConfigurerAdapter { @Override public void configurePathMatch(PathMatchConfigurer configurer) { configurer.setUseSuffixPatternMatch(false); } } 

或者通过像这里的 WebMvcConfigurationSupport。

要添加到skaffman的答案,如果您正在使用并且想要覆盖useDefaultSuffixPattern值,则可以使用以下内容替换标记: