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

nic*_*dos 15 java rest spring spring-mvc

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

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

http://company.com/widgets/123.456.789.500
Run Code Online (Sandbox Code Playgroud)

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

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

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

ska*_*man 12

@PathVariable模式匹配是有点颠簸,当涉及到的URL点(见SPR-5778).你可以通过将useDefaultSuffixPattern属性设置为DefaultAnnotationHandlerMapping来更轻松(但更挑剔),并通过设置属性来更好地控制点重URL false.

如果你还没有DefaultAnnotationHandlerMapping在你的上下文中明确声明a (并且大多数人没有,因为它是为你隐式声明的),那么你可以显式地添加它,并设置该属性.


axt*_*avt 12

可能,这是一个丑陋的黑客,我只是想探索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<String, String> extractUriTemplateVariables(String pattern,
            String path) {
        if (isEncoded(pattern)) {
            pattern = resolvePattern(pattern);
            if (pattern == null) return Collections.emptyMap();
        }
        return target.extractUriTemplateVariables(pattern, path);
    }

    public Comparator<String> getPatternComparator(String pattern) {
        final Comparator<String> targetComparator = target.getPatternComparator(pattern);
        return new Comparator<String>() {
            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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

配置:

<bean id = "pathMatcher" class = "sample.CustomPathMatcher" />

<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name = "pathMatcher" ref="pathMatcher" />
</bean>

<bean class = "org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name = "pathMatcher" ref="pathMatcher" />
</bean>
Run Code Online (Sandbox Code Playgroud)

和用法(给定"/hello/1.2.3",value是"1.2.3"):

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

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


小智 6

<!-- language: lang-java -->
Run Code Online (Sandbox Code Playgroud)

@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


pau*_*lcm 5

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

@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; }
}
Run Code Online (Sandbox Code Playgroud)

您也可以仅附加:.*到您的@RequestMapping,例如"/{documentPath:.*}" (请参阅JIRA评论)