J'ai un contrôleur qui fournit un accès RESTful à 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 de chemin d'accès avec des caractères spéciaux, elle est tronquée. Par exemple: http: // localhost: 8080/blah-server/blah/get/blah2010.08.19-02: 25: 47
Le paramètre blahName sera blah2010.08
Cependant, l'appel à request.getRequestURI () contient toutes les informations transmises.
Avez-vous une idée de la façon d'empêcher Spring de tronquer la @PathVariable?
Essayez une expression régulière pour le @RequestMapping
argument:
RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName:.+}")
Ceci est probablement étroitement lié à SPR-6164 . En bref, le framework essaie d’appliquer quelques intelligences à l’interprétation d’URI, en supprimant ce qu’il considère être des extensions de fichiers. Cela aurait pour effet de tourner blah2010.08.19-02:25:47
en blah2010.08
, puisqu'il pense que le .19-02:25:47
est une extension de fichier.
Comme décrit dans le problème lié, vous pouvez désactiver ce comportement en déclarant votre propre bean DefaultAnnotationHandlerMapping
dans le contexte de l'application et en définissant sa propriété useDefaultSuffixPattern
sur false
. Cela remplacera le comportement par défaut et l'empêchera de molester vos données.
Spring considère que tout ce qui se cache derrière le dernier point est une extension de fichier telle que .json
ou .xml
et le tronquer pour récupérer votre paramètre.
Donc, si vous avez /{blahName}
:
/param
, /param.json
, /param.xml
ou /param.anything
donnera un paramètre de valeur param
/param.value.json
, /param.value.xml
ou /param.value.anything
donnera un paramètre de valeur param.value
Si vous changez votre correspondance en /{blahName:.+}
_ comme suggéré, tout point, y compris le dernier, sera considéré comme faisant partie de votre paramètre:
/param
donnera un paramètre de valeur param
/param.json
donnera un paramètre de valeur param.json
/param.xml
donnera un paramètre de valeur param.xml
/param.anything
donnera un paramètre de valeur param.anything
/param.value.json
donnera un paramètre de valeur param.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
donnera un paramètre de valeur param
/param.value.json
, /param.value.xml
ou /param.value.anything
donnera un paramètre de valeur param.value
Remarque: la différence par rapport à la configuration par défaut n’est visible que si vous avez un mapping tel que /something.{blahName}
. Voir Numéro du projet Resthub .
Si vous souhaitez conserver la gestion des extensions, vous pouvez également, depuis Spring 3.2, définir la propriété useRegisteredSuffixPatternMatch du bean RequestMappingHandlerMapping afin de maintenir la reconnaissance suffixPattern activée, mais limitée à l'extension enregistrée.
Ici, vous définissez uniquement 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 désormais une option contentNegotiation pour fournir un bean personnalisé, mais que la propriété de RequestMappingHandlerMapping doit être remplacée par true (par défaut, false) (cf. https://jira.springsource.org//SPR-7632 ).
Pour cette raison, vous devez toujours remplacer toute la configuration mvc: annotation. J'ai ouvert un ticket à Spring pour demander un RequestMappingHandlerMapping personnalisé: https://jira.springsource.org/browse/SPR-1125 . S'il vous plaît voter si vous êtes intéressé par.
Lors du remplacement, veillez à prendre également en compte le remplacement personnalisé de la gestion d'exécution. Sinon, tous vos mappages d'exceptions personnalisés échoueront. Vous devrez réutiliser messageCoverters avec un bean list:
<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 implémenté, dans le projet open source Resthub dont je fais partie, une série de tests 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 coupé par défaut.
Dans votre print XML config, vous pouvez ajouter DefaultAnnotationHandlerMapping
et définir useDefaultSuffixPattern
sur false
(la valeur par défaut est true
).
Alors ouvrez votre printemps xml mvc-config.xml
(ou quelle que soit sa dénomination) et ajoute
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="useDefaultSuffixPattern" value="false" />
</bean>
Maintenant votre @PathVariable
blahName
(et tous les autres) doit contenir le nom complet, y compris tous les points.
EDIT: Voici un lien vers l'api de printemps
J'ai également rencontré le même problème, et définir la propriété sur false ne m'a pas aidé non plus. Cependant, l'API dit :
Notez que les chemins comportant un suffixe ".xxx" ou se terminant par "/" ne seront déjà pas transformés 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 content de la solution, mais cela a fonctionné.
BTW, je ne sais pas ce que les designers de Spring pensaient en ajoutant cette "fonctionnalité" puis en l'activant par défaut. IMHO, il devrait être supprimé.
Utilisation de la classe de configuration correcte Java:
@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 bidouillage
1) Ajout de HttpServletRequest dans @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()
//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>
Je me suis heurté à cela et les solutions proposées ne fonctionnaient généralement pas comme prévu.
Je suggère d'utiliser une expression SpEL et plusieurs mappages, par exemple.
@RequestMapping(method = RequestMethod.GET,
value = {Routes.BLAH_GET + "/{blahName:.+}",
Routes.BLAH_GET + "/{blahName}/"})
l'ajout du ":. +" a fonctionné pour moi, mais pas avant d'avoir supprimé les accolades extérieures.
value = {"/username/{id:.+}"}
n'a pas fonctionné
value = "/username/{id:.+}"
travaux
J'espère avoir aidé quelqu'un:]
Si vous pouvez modifier l’adresse à laquelle les demandes sont envoyées, une solution simple consisterait à leur ajouter une barre oblique de fin (et également dans le champ @RequestMapping
valeur):
/path/{variable}/
donc la cartographie ressemblerait à:
RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}/")
Voir aussi Spring MVC @PathVariable avec un point (.) Est tronqué .
Le problème d'extension de fichier n'existe que si le paramètre est 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-
Solution de configuration basée sur Java pour empêcher la troncature (en utilisant une classe non dépréciée):
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;
}
}
UPDATE:
J'ai réalisé que la configuration automatique de Spring Boot posait quelques problèmes lorsque j'ai utilisé l'approche ci-dessus (certaines configurations automatiques ne sont pas efficaces).
Au lieu de cela, j'ai commencé à utiliser l'approche BeanPostProcessor
. Cela semblait mieux fonctionner.
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 des extensions 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 solution préférable pour éviter que Spring MVC @PathVariable soit tronqué consiste à ajouter une barre oblique à la fin de la variable de chemin.
Par exemple:
@RequestMapping(value ="/email/{email}/")
Ainsi, la demande ressemblera à:
http://localhost:8080/api/email/[email protected]/
Le problème que vous rencontrez est dû à ressort qui interprète la partie last de l'uri après le point (.) en tant que extension du fichier comme .json ou .xml. Ainsi, lorsque spring essaie de résoudre la variable path, il tronque simplement le reste des données après avoir rencontré un point (.) À la fin de l'URI. Remarque: Ceci ne se produit également que si vous conservez la variable de chemin d'accès à la fin de l'URI.
Par exemple, considérez uri: https: //localhost/example/gallery.df/link.ar
@RestController
public class CustomController {
@GetMapping("/example/{firstValue}/{secondValue}")
public void example(@PathVariable("firstValue") String firstValue,
@PathVariable("secondValue") String secondValue) {
// ...
}
}
Dans l'URL ci-dessus, firstValue = "gallery.df" et secondValue = "link", le dernier bit après le. est tronqué lorsque la variable de chemin est interprétée.
Donc, pour éviter cela, il y a deux manières possibles:
1.) Utilisation d'un mappage d'expressions rationnelles
Utiliser une regex à la fin du mapping
@GetMapping("/example/{firstValue}/{secondValue:.+}")
public void example(
@PathVariable("firstValue") String firstValue,
@PathVariable("secondValue") String secondValue) {
//...
}
En utilisant +, nous indiquons que toute valeur après le point fera également partie de la variable de chemin.
2.) Ajouter une barre oblique à la fin de notre @PathVariable
@GetMapping("/example/{firstValue}/{secondValue}/")
public void example(
@PathVariable("firstValue") String firstValue,
@PathVariable("secondValue") String secondValue) {
//...
}
Ceci inclura notre deuxième variable le protégeant du comportement par défaut de Spring.
3) En remplaçant la configuration webmvc par défaut de Spring
Spring fournit des moyens de remplacer les configurations par défaut importées à l'aide des annotations @ EnableWebMvc. Nous pouvons personnaliser la configuration de Spring MVC en déclarant notre propre DefaultAnnotationHandlerMapping son bean dans le contexte de l'application et en définissant son useDefaultSuffixPattern propriété à false. Exemple:
@Configuration
public class CustomWebConfiguration extends WebMvcConfigurationSupport {
@Bean
public RequestMappingHandlerMapping
requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping
= super.requestMappingHandlerMapping();
handlerMapping.setUseSuffixPatternMatch(false);
return handlerMapping;
}
}
Gardez à l'esprit que le remplacement de cette configuration par défaut affecte toutes les URL.
Remarque: Nous étendons ici la classe WebMvcConfigurationSupport pour remplacer les méthodes par défaut. Il existe un autre moyen de remplacer les configurations de deault en implémentant l'interface WebMvcConfigurer. Pour plus de détails à ce sujet, lisez: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html