Printemps MVC @PathVariable obtenir tronqué
j'ai un contrôleur qui fournit un accès tranquille à l'information:
@RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}")
public ModelAndView getBlah(@PathVariable String blahName, HttpServletRequest request,
HttpServletResponse response) {
le problème que je rencontre est que si je frappe le serveur avec une variable path avec des caractères spéciaux, il est tronqué. Exemple: http://localhost:8080/blah-server/blah/get/blah2010.08.19-02:25:47
le paramètre blahName sera blah2010.08
cependant, l'appel à la demande.getRequestURI() contient toutes les informations transmises.
comment empêcher le ressort de tronquer le @PathVariable?
15 réponses
essayez une expression régulière pour l'argument @RequestMapping
:
RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName:.+}")
il s'agit probablement d'une relation étroite avec SPR-6164 . En bref, le framework essaie d'appliquer quelques astuces à L'interprétation de L'URI, en supprimant ce qu'il pense être des extensions de fichier. Cela aurait pour effet de transformer blah2010.08.19-02:25:47
en blah2010.08
, puisqu'il pense que .19-02:25:47
est une extension de fichier.
Comme décrit dans la question liée, vous pouvez désactiver ce comportement en déclarant votre propre DefaultAnnotationHandlerMapping
bean dans le contexte de l'application, et établissant sa propriété useDefaultSuffixPattern
à false
. Cela annulera le comportement par défaut, et l'empêchera de molester vos données.
Spring considère que tout ce qui se trouve derrière le dernier point est une extension de fichier comme .json
ou .xml
et le tronque pour récupérer votre paramètre.
donc si vous avez /{blahName}
:
-
/param
,/param.json
,/param.xml
ou/param.anything
résultera en un paramètre avec la valeurparam
-
/param.value.json
,/param.value.xml
ou/param.value.anything
résultera en un param avec valeurparam.value
si vous changez votre mapping en /{blahName:.+}
comme suggéré, tout point, y compris le dernier, sera considéré comme faisant partie de votre paramètre:
-
/param
résultera en un paramètre avec la valeurparam
-
/param.json
résultera en un paramètre avec la valeurparam.json
-
/param.xml
résultera en un paramètre avec la valeurparam.xml
-
/param.anything
résultera en un paramètre avec la valeurparam.anything
-
/param.value.json
résultera en un paramètre avec la valeurparam.value.json
- ...
si vous ne vous souciez pas de la reconnaissance d'extension, vous pouvez la désactiver en remplaçant 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>
donc, encore une fois, si vous avez /{blahName}
:
-
/param
,/param.json
,/param.xml
ou/param.anything
résultera en un paramètre avec la valeurparam
-
/param.value.json
,/param.value.xml
ou/param.value.anything
résultera en un paramètre avec la valeurparam.value
Note: la différence par rapport à la configuration par défaut n'est visible que si vous avez une correspondance comme /something.{blahName}
. Voir numéro du projet Resthub .
si vous souhaitez garder la gestion de l'extension, depuis le printemps 3.2 vous pouvez également définir la propriété useRegisteredSuffixPatternMatch de RequestMappingHandlerMapping bean afin de maintenir la reconnaissance de suffixPattern activée mais limitée à l'extension enregistrée.
ici vous définissez seulement les extensions JSON et xml:
<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>
notez que mvc: annotation-driven accepte maintenant une option contentNegotiation pour fournir un bean personnalisé mais la propriété de RequestMappingHandlerMapping doit être changée en true (false par défaut)) (cf. https://jira.springsource.org/browse/SPR-7632 ).
pour cette raison, vous devez toujours annuler toute la configuration mvc:annotation-driven. J'ai ouvert un billet pour Spring pour demander une demande personnaliséemappinghandlermapping: https://jira.springsource.org/browse/SPR-11253 . Merci de voter si vous êtes intéressé dans.
tout en dépassant, attention à prendre également en compte la gestion de l'exécution sur mesure primordial. Sinon, tous vos mappages d'Exception personnalisés échoueront. Vous devrez réutiliser les décodeurs de messagerie avec un haricot de la liste:
<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>
j'ai mis en œuvre, dans le projet open source Resthub dont je fais partie, un ensemble d'essais sur ces sujets: Voir https://github.com/resthub/resthub-spring-stack/pull/219/files et https://github.com/resthub/resthub-spring-stack/issues/217
Tout ce qui suit le dernier point est interprété comme une extension de fichier et est coupé par défaut.
Dans votre config xml du printemps, vous pouvez ajouter DefaultAnnotationHandlerMapping
et définir useDefaultSuffixPattern
à false
(par défaut true
).
ouvrez donc votre ressort xml mvc-config.xml
(ou comment il s'appelle) et ajoutez
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="useDefaultSuffixPattern" value="false" />
</bean>
maintenant votre @PathVariable
blahName
(et tous les autres, aussi) devrait contenir le nom complet incluant tous les points.
EDIT: voici un lien vers l'api du ressort
j'ai aussi rencontré le même problème, et mettre la propriété à false ne m'a pas aidé non plus. Cependant, L'API dit :
Note que les chemins qui comprennent un ".xxx "suffixe ou fin avec" / " déjà ne sera pas transformé en utilisant le modèle de suffixe par défaut dans tous les cas.
j'ai essayé d'ajouter" /end " à mon URL RESTful, et le problème a disparu. Je ne suis pas satisfait de la solution, mais ça a marché.
BTW, Je ne sais pas ce que les concepteurs du ressort pensaient quand ils ont ajouté cette "fonctionnalité" et l'ont ensuite activée par défaut. À mon humble avis, il doit être retiré.
utilisant la classe de configuration Java correcte:
@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);
}
}
j'ai résolu par ce hack
1) Ajouté HttpServletRequest in @PathVariable comme ci-dessous
@PathVariable("requestParam") String requestParam, HttpServletRequest request) throws Exception {
2) Obtenir l'URL directement (à ce niveau Pas de troncature) dans la requête
request.getPathInfo()
je viens de tomber sur ceci et les solutions ici n'ont généralement pas fonctionné comme je l'espérais.
je suggère d'utiliser une expression de SpEL et des mappages multiples, par exemple
@RequestMapping(method = RequestMethod.GET,
value = {Routes.BLAH_GET + "/{blahName:.+}",
Routes.BLAH_GET + "/{blahName}/"})
le problème d'extension de fichier n'existe que si le paramètre se trouve dans la dernière partie de l'URL. Changement
@RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}")
à
@RequestMapping(
method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}/safe")
et tout ira bien à nouveau-
Si vous pouvez modifier l'adresse que les demandes sont envoyées à, la solution simple serait d'ajouter une barre oblique (et aussi dans le @RequestMapping
):
/path/{variable}/
donc la carte ressemblerait à:
RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}/")
Voir aussi Spring MVC @PathVariable avec le point (.) devient tronqué .
//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>
ajouter ":.+ "a fonctionné pour moi, mais pas avant que j'ai enlevé les crochets bouclés extérieurs.
value = {"/username/{id:.+}"}
n'a pas fonctionné
value = "/username/{id:.+}"
œuvres
Hope I help someone :]
Java configuration basée sur une solution pour éviter la troncature (à l'aide d'un non-déconseillé de classe):
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;
}
}
mise à jour:
j'ai réalisé avoir quelques problèmes avec L'auto-configuration de démarrage à ressort quand j'ai utilisé l'approche ci-dessus (certains auto-configuration ne reçoit pas efficace.)
à la place, j'ai commencé à utiliser l'approche BeanPostProcessor
. Il semblait être à travailler mieux.
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);
}
}
inspiré de: http://ronaldxq.blogspot.com/2014/10/spring-mvc-setting-alwaysusefullpath-on.html
si vous êtes sûr que votre texte ne correspond à aucune extension par défaut, vous pouvez utiliser le code ci-dessous:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseRegisteredSuffixPatternMatch(true);
}
}
Ma meilleure solution pour empêcher le ressort MVC @PathVariable d'être tronqué est d'ajouter slash arrière à la fin de la variable path.
par exemple:
@RequestMapping(value ="/email/{email}/")
ainsi, la demande ressemblera à:
http://localhost:8080/api/email/test@test.com/