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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2079299/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 19:18:36  来源:igfitidea点击:

Trying to create REST-ful URLs with multiple dots in the "filename" part - Spring 3.0 MVC

javaspringrestspring-mvc

提问by nickdos

I'm using Spring MVC (3.0) with annotation-driven controllers. I would like to create REST-ful URLs for resources and be able to notrequire (but still optionally allow) file extension on the end of the URL (but assume HTML content type if no extension). This works out-of-the-box with Spring MVC as long as there are no dots (period/full-stop) in the filename part.

我正在使用带有注释驱动控制器的 Spring MVC (3.0)。我想为资源创建REST-FUL网址,而且能够要求(但仍可选允许)的URL的末尾文件扩展名(但如果没有扩展假设HTML内容类型)。只要文件名部分中没有点(句点/句号),这对 Spring MVC 来说是开箱即用的。

However some of my URLs require an identifier with dots in the name. E.g. like this:

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

http://company.com/widgets/123.456.789.500

In this case Spring looks for a content type for the extension .500and finds none so errors. I can use work-arounds like adding .htmlto the end, encoding the identifier or adding a trailing slash. I'm not happy with any if these but could probably live with adding .html.

在这种情况下,Spring 会查找扩展的内容类型,但.500没有发现任何错误。我可以使用变通方法,例如添加.html到末尾、编码标识符或添加尾部斜杠。我对这些都不满意,但可能会添加.html.

I've unsuccessfully looked for a way of overriding the default file extension detection in Spring.

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

Is it possible to customize or disable file extension detection for a given controller method or URL pattern, etc?

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

采纳答案by skaffman

The @PathVariablepattern matching is a bit twitchy when it comes to dots in the URL (see SPR-5778). You can make it less twitchy (but more picky), and get better control over dot-heavy URLs, by setting the useDefaultSuffixPatternproperty on DefaultAnnotationHandlerMappingto false.

@PathVariable模式匹配是有点颠簸,当涉及到的URL点(见SPR-5778)。你可以把它颠簸少(但更挑剔),并获得过点沉重的URL更好的控制,通过设置useDefaultSuffixPattern的属性DefaultAnnotationHandlerMappingfalse

If you haven't already explicitly declared a DefaultAnnotationHandlerMappingin your context (and most people don't since it's declared implicitly for you), then you can add it explicitly, and set that property.

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

回答by axtavt

Probably, it's an ugly hack, I just wanted to explore extensibility of Spring @MVC. Here is a customized PathMatcher. It uses $in the pattern as the end marker - if pattern ends with it, marker is removed and pattern is matched by the default matcher, but if pattern has $in the middle (e.g. ...$.*), such a pattern is not matched.

也许,这是一个丑陋的黑客,我只是想探索 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;
        }
    }
}

Config:

配置:

<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>

And usage (given "/hello/1.2.3", valueis "1.2.3"):

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

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

EDIT:: Now doesn't break "trailing slash doesn't matter" rule

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

回答by axtavt

<!-- language: lang-java -->

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

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

// Above code should match /widgets/111.222.333.444

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

回答by paulcm

Spring 3.2 has changed, and suggests that you set properties on the RequestMappingHandlerMappingbean, either explicitly (if not using the mvc namespace) or by using a BeanPostProcessorsuch as the following (you'll need to scan or instantiate it):

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; }
}

You can also just append :.*to your @RequestMapping, e.g. "/{documentPath:.*}"(see JIRA comment)

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

回答by Jarppe

I had the same problem and I also solved it by custom PathMatcher. My solution is somewhat simplier to what axtavt proposed. My PathMatcher also has a private final AntPathMatcher target, and it delegates all calls to it unchanged, except for match() method:

我遇到了同样的问题,我也通过自定义 PathMatcher 解决了它。我的解决方案比 axtavt 提出的要简单一些。我的 PathMatcher 也有一个私有的最终 AntPathMatcher 目标,它将所有调用委托给它,除了 match() 方法:

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

This works because Spring tries to match controllers by adding "." to the end. For example, with path mapping "/widgets/{id}" and URL "/widgets/1.2.3.4", Spring tries first match againts "/widgets/{id}." and after that "/widgets/{id}". The first will match, but it leaves only "1.2.3" for id.

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

My PatchMatcher specifically rejects patterns ending ".*", thus the first attempt fails and the second matches.

我的 PatchMatcher 特别拒绝以“.*”结尾的模式,因此第一次尝试失败,第二次匹配。

If you are using ContentNegotiatingViewResolver you can still specify content type in URL's using request parameter "format" (if the favorParameter is set to true).

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

-jarppe

-jarppe

回答by pls

JFY: in Spring 4this issue is fixed via: WebMvcConfigurerAdapter.

JFY:在Spring 4 中,此问题通过以下方式修复:WebMvcConfigurerAdapter。

@Configuration 
class MvcConfiguration extends WebMvcConfigurerAdapter {

@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
    configurer.setUseSuffixPatternMatch(false);
}
}

Or via WebMvcConfigurationSupport like here.

或者通过 WebMvcConfigurationSupport 像这里

and in Spring 5:

并在春季 5

@Configuration
public class MvcConfiguration implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseSuffixPatternMatch(false);
    }
}

回答by Templar

To add to skaffman's answer, if you're using <mvc:annotation-driven/>and you want to override the useDefaultSuffixPattern value, you can replace the <mvc:annotation-driven>tag with the following:

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

<!-- Maps requests to @Controllers based on @RequestMapping("path") annotation values -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="order" value="1" />
    <property name="useDefaultSuffixPattern" value="false" />
</bean>

<!-- Enables annotated @Controllers -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />