Spring MVC @PathVariable with dot (.) is getting t

2018-12-31 13:15发布

问题:

This is continuation of question Spring MVC @PathVariable getting truncated

Spring forum states that it has fixed(3.2 version) as part of ContentNegotiationManager. see the below link.
https://jira.springsource.org/browse/SPR-6164
https://jira.springsource.org/browse/SPR-7632

In my application requestParameter with .com is truncated.

Could anyone explain me how to use this new feature? how is it configurable at xml?

Note: spring forum- #1 Spring MVC @PathVariable with dot (.) is getting truncated

回答1:

As far as i know this issue appears only for the pathvariable at the end of the requestmapping.

We were able to solve that by defining the regex addon in the requestmapping.

 /somepath/{variable:.+}


回答2:

Spring considers that anything behind the last dot is a file extension such as .jsonor .xml and trucate it to retrieve your parameter.

So if you have /somepath/{variable} :

  • /somepath/param, /somepath/param.json, /somepath/param.xml or /somepath/param.anything will result in a param with value param
  • /somepath/param.value.json, /somepath/param.value.xml or /somepath/param.value.anything will result in a param with value param.value

if you change your mapping to /somepath/{variable:.+} as suggested, any dot, including the last one will be consider as part of your parameter :

  • /somepath/param will result in a param with value param
  • /somepath/param.json will result in a param with value param.json
  • /somepath/param.xml will result in a param with value param.xml
  • /somepath/param.anything will result in a param with value param.anything
  • /somepath/param.value.json will result in a param with value param.value.json
  • ...

If you don\'t care of extension recognition, you can disable it by overriding mvc:annotation-driven automagic :

<bean id=\"handlerMapping\"
      class=\"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping\">
    <property name=\"contentNegotiationManager\" ref=\"contentNegotiationManager\"/>
    <property name=\"useSuffixPatternMatch\" value=\"false\"/>
</bean>

So, again, if you have /somepath/{variable} :

  • /somepath/param, /somepath/param.json, /somepath/param.xml or /somepath/param.anything will result in a param with value param
  • /somepath/param.value.json, /somepath/param.value.xml or /somepath/param.value.anything will result in a param with value param.value

note : the difference from the default config is visible only if you have a mapping like somepath/something.{variable}. see Resthub project issue

if you want to keep extension management, since Spring 3.2 you can also set the useRegisteredSuffixPatternMatch property of RequestMappingHandlerMapping bean in order to keep suffixPattern recognition activated but limited to registered extension.

Here you define only json and xml extensions :

<bean id=\"handlerMapping\"
      class=\"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping\">
    <property name=\"contentNegotiationManager\" ref=\"contentNegotiationManager\"/>
    <property name=\"useRegisteredSuffixPatternMatch\" value=\"true\"/>
</bean>

<bean id=\"contentNegotiationManager\" class=\"org.springframework.web.accept.ContentNegotiationManagerFactoryBean\">
    <property name=\"favorPathExtension\" value=\"false\"/>
    <property name=\"favorParameter\" value=\"true\"/>
    <property name=\"mediaTypes\">
        <value>
            json=application/json
            xml=application/xml
        </value>
    </property>
</bean>

Note that mvc:annotation-driven accepts now a contentNegotiation option to provide a custom bean but the property of RequestMappingHandlerMapping has to be changed to true (default false) (cf. https://jira.springsource.org/browse/SPR-7632).

For that reason, you still have to override the all mvc:annotation-driven configuration. I opened a ticket to Spring to ask for a custom RequestMappingHandlerMapping : https://jira.springsource.org/browse/SPR-11253. Please vote if you are intereted in.

While overriding, be carreful to consider also custom Execution management overriding. Otherwise, all your custom Exception mappings will fail. You will have to reuse messageCoverters with a list bean :

<bean id=\"validator\" class=\"org.springframework.validation.beanvalidation.LocalValidatorFactoryBean\" />
<bean id=\"conversionService\" class=\"org.springframework.format.support.FormattingConversionServiceFactoryBean\" />

<util:list id=\"messageConverters\">
    <bean class=\"your.custom.message.converter.IfAny\"></bean>
    <bean class=\"org.springframework.http.converter.ByteArrayHttpMessageConverter\"></bean>
    <bean class=\"org.springframework.http.converter.StringHttpMessageConverter\"></bean>
    <bean class=\"org.springframework.http.converter.ResourceHttpMessageConverter\"></bean>
    <bean class=\"org.springframework.http.converter.xml.SourceHttpMessageConverter\"></bean>
    <bean class=\"org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter\"></bean>
    <bean class=\"org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter\"></bean>
    <bean class=\"org.springframework.http.converter.json.MappingJacksonHttpMessageConverter\"></bean>
</util:list>

<bean name=\"exceptionHandlerExceptionResolver\"
      class=\"org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver\">
    <property name=\"order\" value=\"0\"/>
    <property name=\"messageConverters\" ref=\"messageConverters\"/>
</bean>

<bean name=\"handlerAdapter\"
      class=\"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter\">
    <property name=\"webBindingInitializer\">
        <bean class=\"org.springframework.web.bind.support.ConfigurableWebBindingInitializer\">
            <property name=\"conversionService\" ref=\"conversionService\" />
            <property name=\"validator\" ref=\"validator\" />
        </bean>
    </property>
    <property name=\"messageConverters\" ref=\"messageConverters\"/>
</bean>

<bean id=\"handlerMapping\"
      class=\"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping\">
</bean>

I implemented, in the open source project Resthub that I am part of, a set of tests on these subjects : see https://github.com/resthub/resthub-spring-stack/pull/219/files & https://github.com/resthub/resthub-spring-stack/issues/217



回答3:

Update for Spring 4: since 4.0.1 you can use PathMatchConfigurer (via your WebMvcConfigurer), e.g.

@Configuration
protected static class AllResources extends WebMvcConfigurerAdapter {

    @Override
    public void configurePathMatch(PathMatchConfigurer matcher) {
        matcher.setUseRegisteredSuffixPatternMatch(true);
    }

}

In xml, it would be (https://jira.spring.io/browse/SPR-10163):

<mvc:annotation-driven>
    [...]
    <mvc:path-matching registered-suffixes-only=\"true\"/>
</mvc:annotation-driven>


回答4:

In addition to Martin Frey\'s answer, this can also be fixed by adding a trailing slash in the RequestMapping value:

/path/{variable}/

Keep in mind that this fix does not support maintainability. It now requires all URI\'s to have a trailing slash - something that may not be apparent to API users / new developers. Because it\'s likely not all parameters may have a . in them, it may also create intermittent bugs



回答5:

adding the \":.+\" worked for me, but not until I removed outer curly brackets.

value = {\"/username/{id:.+}\"} didn\'t work

value = \"/username/{id:.+}\" works

Hope I helped someone :)



回答6:

In Spring Boot Rest Controller, I have resolved these by following Steps:

RestController :

@GetMapping(\"/statusByEmail/{email:.+}/\")
public String statusByEmail(@PathVariable(value = \"email\") String email){
  //code
}

And From Rest Client:

Get http://mywebhook.com/statusByEmail/abc.test@gmail.com/


回答7:

/somepath/{variable:.+} works in Java requestMapping tag.



回答8:

Here\'s an approach that relies purely on java configuration:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

@Configuration
public class MvcConfig extends WebMvcConfigurationSupport{

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
        handlerMapping.setUseSuffixPatternMatch(false);
        handlerMapping.setUseTrailingSlashMatch(false);
        return handlerMapping;
    }
}


回答9:

One pretty easy way to work around this issue is to append a trailing slash ...

e.g.:

use :

/somepath/filename.jpg/

instead of:

/somepath/filename.jpg


回答10:

In Spring Boot, The Regular expression solve the problem like

@GetMapping(\"/path/{param1:.+}\")


回答11:

The complete solution including email addresses in path names for spring 4.2 is

<bean id=\"contentNegotiationManager\"
    class=\"org.springframework.web.accept.ContentNegotiationManagerFactoryBean\">
    <property name=\"favorPathExtension\" value=\"false\" />
    <property name=\"favorParameter\" value=\"true\" />
    <property name=\"mediaTypes\">
        <value>
            json=application/json
            xml=application/xml
        </value>
    </property>
</bean>
<mvc:annotation-driven
    content-negotiation-manager=\"contentNegotiationManager\">
    <mvc:path-matching suffix-pattern=\"false\" registered-suffixes-only=\"true\" />
</mvc:annotation-driven>

Add this to the application-xml



回答12:

If you are using Spring 3.2.x and <mvc:annotation-driven />, create this little BeanPostProcessor:

package spring;

public final class DoNotTruncateMyUrls implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof RequestMappingHandlerMapping) {
            ((RequestMappingHandlerMapping)bean).setUseSuffixPatternMatch(false);
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

Then put this in your MVC config xml:

<bean class=\"spring.DoNotTruncateMyUrls\" />


回答13:

Finally I found solution in Spring Docs:

To completely disable the use of file extensions, you must set both of the following:

 useSuffixPatternMatching(false), see PathMatchConfigurer

 favorPathExtension(false), see ContentNegotiationConfigurer

Adding this to my WebMvcConfigurerAdapter implementation solved the problem:

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    configurer.favorPathExtension(false);
}

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


回答14:

For me the

@GetMapping(path = \"/a/{variableName:.+}\")

does work but only if you also encode the \"dot\" in your request url as \"%2E\" then it works. But requires URL\'s to all be that...which is not a \"standard\" encoding, though valid. Feels like something of a bug :|

The other work around, similar to the \"trailing slash\" way is to move the variable that will have the dot \"inline\" ex:

@GetMapping(path = \"/{variableName}/a\")

now all dots will be preserved, no modifications or regex\'s needed.