可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a controller that provides RESTful access to information:
@RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + \"/{blahName}\")
public ModelAndView getBlah(@PathVariable String blahName, HttpServletRequest request,
HttpServletResponse response) {
The problem I am experiencing is that if I hit the server with a path variable with special characters it gets truncated. For example:
http://localhost:8080/blah-server/blah/get/blah2010.08.19-02:25:47
The parameter blahName will be blah2010.08
However, the call to request.getRequestURI() contains all the information passed in.
Any idea how to prevent Spring from truncating the @PathVariable?
回答1:
Try a regular expression for the @RequestMapping
argument:
RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + \"/{blahName:.+}\")
回答2:
This is probably closely related to SPR-6164. Briefly, the framework tries to apply some smarts to the URI interpretation, removing what it thinks are file extensions. This would have the effect of turning blah2010.08.19-02:25:47
into blah2010.08
, since it thinks the .19-02:25:47
is a file extension.
As described in the linked issue, you can disable this behaviour by declaring your own DefaultAnnotationHandlerMapping
bean in the app context, and setting its useDefaultSuffixPattern
property to false
. This will override the default behaviour, and stop it molesting your data.
回答3:
Spring considers that anything behind the last dot is a file extension such as .json
or .xml
and truncate it to retrieve your parameter.
So if you have /{blahName}
:
/param
, /param.json
, /param.xml
or /param.anything
will result in a param with value param
/param.value.json
, /param.value.xml
or /param.value.anything
will result in a param with value param.value
If you change your mapping to /{blahName:.+}
as suggested, any dot, including the last one, will be considered as part of your parameter:
/param
will result in a param with value param
/param.json
will result in a param with value param.json
/param.xml
will result in a param with value param.xml
/param.anything
will result in a param with value param.anything
/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 /{blahName}
:
/param
, /param.json
, /param.xml
or /param.anything
will result in a param with value param
/param.value.json
, /param.value.xml
or /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 /something.{blahName}
. 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 all the 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 interested in.
While overriding, be careful 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 and https://github.com/resthub/resthub-spring-stack/issues/217
回答4:
Everything after the last dot is interpreted as file extension and cut off by default.
In your spring config xml you can add DefaultAnnotationHandlerMapping
and set useDefaultSuffixPattern
to false
(default is true
).
So open your spring xml mvc-config.xml
(or however it is called) and add
<bean class=\"org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping\">
<property name=\"useDefaultSuffixPattern\" value=\"false\" />
</bean>
Now your @PathVariable
blahName
(and all other, too) should contain the full name including all dots.
EDIT: Here is a link to the spring api
回答5:
I also ran into the same issue, and setting the property to false didn\'t help me either. However, the API says:
Note that paths which include a \".xxx\" suffix or end with \"/\" already
will not be transformed using the default suffix pattern in any case.
I tried adding \"/end\" to my RESTful URL, and the problem went away. I\'m not please with the solution, but it did work.
BTW, I don\'t know what the Spring designers were thinking when they added this \"feature\" and then turned it on by default. IMHO, it should be removed.
回答6:
Using the correct Java configuration class :
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter
{
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer)
{
configurer.favorPathExtension(false);
}
@Override
public void configurePathMatch(PathMatchConfigurer configurer)
{
configurer.setUseSuffixPatternMatch(false);
}
}
回答7:
I resolved by this hack
1) Added HttpServletRequest in @PathVariable like below
@PathVariable(\"requestParam\") String requestParam, HttpServletRequest request) throws Exception {
2) Get the URL directly (At this level no truncation) in the request
request.getPathInfo()
Spring MVC @PathVariable with dot (.) is getting truncated
回答8:
I just ran into this and the solutions here didn\'t generally work as I expected.
I suggest using a SpEL expression and multiple mappings, e.g.
@RequestMapping(method = RequestMethod.GET,
value = {Routes.BLAH_GET + \"/{blahName:.+}\",
Routes.BLAH_GET + \"/{blahName}/\"})
回答9:
The file extension problem only exists if the parameter is in the last part of the URL. Change
@RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + \"/{blahName}\")
to
@RequestMapping(
method = RequestMethod.GET, value = Routes.BLAH_GET + \"/{blahName}/safe\")
and all will be well again-
回答10:
If you can edit the address that requests are sent to, simple fix would be to add a trailing slash to them (and also in the @RequestMapping
value):
/path/{variable}/
so the mapping would look like:
RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + \"/{blahName}/\")
See also Spring MVC @PathVariable with dot (.) is getting truncated.
回答11:
//in your xml dispatcher add this property to your default annotation mapper bean as follow
<bean class=\"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping\">
<property name=\"alwaysUseFullPath\" value=\"true\"></property>
</bean>
回答12:
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 :]
回答13:
Java based configuration solution to prevent truncation (using a not-deprecated class):
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 PolRepWebConfig extends WebMvcConfigurationSupport {
@Override
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
final RequestMappingHandlerMapping handlerMapping = super
.requestMappingHandlerMapping();
// disable the truncation after .
handlerMapping.setUseSuffixPatternMatch(false);
// disable the truncation after ;
handlerMapping.setRemoveSemicolonContent(false);
return handlerMapping;
}
}
Source: http://www.javacodegeeks.com/2013/01/spring-mvc-customizing-requestmappinghandlermapping.html
UPDATE:
I realized having some problems with Spring Boot auto-configuration when I used the approach above (some auto-configuration doesn\'t get effective).
Instead, I started to use the BeanPostProcessor
approach. It seemed to be working better.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
private static final Logger logger = LoggerFactory
.getLogger(MyBeanPostProcessor.class);
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof RequestMappingHandlerMapping) {
setRemoveSemicolonContent((RequestMappingHandlerMapping) bean,
beanName);
setUseSuffixPatternMatch((RequestMappingHandlerMapping) bean,
beanName);
}
return bean;
}
private void setRemoveSemicolonContent(
RequestMappingHandlerMapping requestMappingHandlerMapping,
String beanName) {
logger.info(
\"Setting \'RemoveSemicolonContent\' on \'RequestMappingHandlerMapping\'-bean to false. Bean name: {}\",
beanName);
requestMappingHandlerMapping.setRemoveSemicolonContent(false);
}
private void setUseSuffixPatternMatch(
RequestMappingHandlerMapping requestMappingHandlerMapping,
String beanName) {
logger.info(
\"Setting \'UseSuffixPatternMatch\' on \'RequestMappingHandlerMapping\'-bean to false. Bean name: {}\",
beanName);
requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
}
}
Inspired from: http://ronaldxq.blogspot.com/2014/10/spring-mvc-setting-alwaysusefullpath-on.html
回答14:
if you are sure that your text will not match any of default extensions you can use below code:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseRegisteredSuffixPatternMatch(true);
}
}
回答15:
My preferable solution to prevent the Spring MVC @PathVariable to get truncated is to add trailing slash at the end of the path variable.
For example:
@RequestMapping(value =\"/email/{email}/\")
So, the request will look like:
http://localhost:8080/api/email/test@test.com/