尝试在“文件名”部分创建具有多个点的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值,则可以使用以下内容替换
标记: