2010-08-19 18 views
124

Ho un controller che fornisce l'accesso alle informazioni RESTful:Spring MVC @PathVariable ottenere troncato

@RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}") 
public ModelAndView getBlah(@PathVariable String blahName, HttpServletRequest request, 
          HttpServletResponse response) { 

Il problema che sto vivendo è che se ho colpito il server con una variabile percorso con caratteri speciali che venga troncato. Per esempio: http://localhost:8080/blah-server/blah/get/blah2010.08.19-02:25:47

Il parametro blahName sarà blah2010.08

Tuttavia, la chiamata a request.getRequestURI() contiene tutte le informazioni passate in

Qualsiasi idea di come prevenire primavera da troncare. il parametro @PathVariable?

+0

Sembra che questo è stato risolto nella primavera del 3,2-M2: vedi [Consenti percorsi di estensione di file validi per la negoziazione dei contenuti da specificare] (https://jira.springsource.org/browse/SPR-7632) e [la sua documentazione] (http://static.springsource.org/spring/docs/3.2.0.M2/reference/htmlsingle/# MVC-config-content-negoziazione). – Arjan

risposta

137

Prova un'espressione regolare per l'argomento @RequestMapping:

RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName:.+}") 
+1

Grazie per la risposta, questo mi ha aiutato a risolvere un caso in cui i nomi utente sono stati tagliati in qualche modo ..(-: L'altra opzione con "useDefaultSuffixPattern" non era un'opzione perché stiamo usando le classi spring @Configuration invece di XML. – evandongen

+3

Funziona, ma qual è il significato dei due punti nella regex? –

+6

Noah, I haven ' Ho usato questo da molto tempo, ma penso che i due punti separino l'espressione regolare dal nome dell'argomento per legarlo a. – earldouglas

54

Questo è probabilmente strettamente legato al SPR-6164. In breve, il framework tenta di applicare alcune intelligenze all'interpretazione URI, rimuovendo ciò che pensa siano estensioni di file. Ciò avrebbe l'effetto di trasformare blah2010.08.19-02:25:47 in blah2010.08, dal momento che ritiene che .19-02:25:47 sia un'estensione di file.

Come descritto nel problema collegato, è possibile disabilitare questo comportamento dichiarando il proprio bean DefaultAnnotationHandlerMapping nel contesto dell'app e impostando la proprietà useDefaultSuffixPattern su false. Ciò sostituirà il comportamento predefinito e impedirà il molesting dei dati.

+2

Attivare la negoziazione del contenuto basato sull'estensione per impostazione predefinita sembra una scelta così strana: quanti sistemi in realtà espongono la stessa risorsa in diversi formati? – Affe

+0

Ho provato questa mattina e aveva ancora variabili di percorso troncate – phogel

+1

Questo ha funzionato per me con un problema simile. Grazie, skaffman. – AHungerArtist

15

Tutto dopo l'ultimo punto viene interpretato come estensione del file e disattivato per impostazione predefinita.
Nel file XML di configurazione primavera è possibile aggiungere DefaultAnnotationHandlerMapping e impostare useDefaultSuffixPattern su false (il valore predefinito è true).

Quindi aprire il tuo xml primavera mvc-config.xml (o comunque si chiama) e aggiungere

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> 
    <property name="useDefaultSuffixPattern" value="false" /> 
</bean> 

Ora il vostro @PathVariableblahName (e tutti gli altri, troppo) dovrebbe contenere il nome completo, compreso tutti i punti.

EDIT: Ecco un link to the spring api

+0

Non ho provato, ma [altri sostengono] (http://stackoverflow.com/questions/4135329/how-to-change-spring-mvcs-behavior-in-handling-url-dot-character/11141184#11141184) dovresti anche rimuovere '' se applicabile. – Arjan

3

Ho appena imbattuto in questo e le soluzioni qui non ha generalmente funzionato come mi aspettavo.

Suggerisco di utilizzare un'espressione SpEL e più mappature, ad es.

@RequestMapping(method = RequestMethod.GET, 
    value = {Routes.BLAH_GET + "/{blahName:.+}", 
      Routes.BLAH_GET + "/{blahName}/"}) 
7

Ho riscontrato anche lo stesso problema e l'impostazione della proprietà su false non mi è stata d'aiuto. Tuttavia, the API says:

Nota che i percorsi che includono un suffisso ".xxx" o terminare con '/' già non sarà trasformato utilizzando il modello suffisso predefinito in ogni caso.

Ho provato ad aggiungere "/ end" al mio URL RESTful e il problema è andato via. Non sono per favore con la soluzione, ma ha funzionato.

BTW, non so cosa stessero pensando i progettisti di Spring quando hanno aggiunto questa "funzione" e quindi l'hanno attivata per impostazione predefinita. IMHO, dovrebbe essere rimosso.

+0

Sono d'accordo. Di recente sono stato morso da questo. – llambda

4

ho risolto da questo hack

1) Aggiunto HttpServletRequest in @PathVariable come qui di seguito

@PathVariable("requestParam") String requestParam, HttpServletRequest request) throws Exception { 

2) ottenere l'URL direttamente (A questo livello non troncamento) nella richiesta

request.getPathInfo() 

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

3

Il problema dell'estensione del file esiste solo se il parametro si trova nell'ultima parte dell'URL. Cambiare

@RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}") 

a

@RequestMapping(
    method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}/safe") 

e tutto andrà bene nuovo-

3

Se è possibile modificare l'indirizzo che le richieste vengono inviate a, semplice correzione sarebbe quella di aggiungere una barra finale a loro (e anche nel valore @RequestMapping):

/path/{variable}/ 

quindi la mappatura sarà simile:

RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}/") 

Vedere anche Spring MVC @PathVariable with dot (.) is getting truncated.

27

Spring ritiene che qualsiasi elemento dietro l'ultimo punto sia un'estensione di file come .json o .xml e lo tronca per recuperare il parametro.

Quindi, se avete /{blahName}:

  • /param, /param.json, /param.xml o /param.anything si tradurrà in un param con un valore param
  • /param.value.json, /param.value.xml o /param.value.anything si tradurrà in un param con un valore param.value

Se si modifica la mappatura su /{blahName:.+} come suggerito, qualsiasi punto, compreso l'ultimo, sarà considerata come parte del vostro parametro:

  • /param si tradurrà in un param con un valore param
  • /param.json si tradurrà in un param con un valore param.json
  • /param.xml si tradurrà in un parametro con valore param.xml
  • /param.anything si tradurrà in un parametro con valore param.anything
  • /param.value.json si tradurrà in un param con un valore param.value.json
  • ...

Se non si cura del riconoscimento di estensione, è possibile disattivarla mediante l'override 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> 

Quindi, ancora una volta, se avete /{blahName}:

  • /param, /param.json, /param.xml o /param.anything si tradurrà in un param con un valore param
  • /param.value.json, /param.value.xml o /param.value.anything si tradurrà in un param con un valore param.value

Nota: la differenza dalla configurazione di default è visibile solo se si dispone di un mappatura come /something.{blahName}. Vedi Resthub project issue.

Se si desidera mantenere la gestione delle estensioni, dalla primavera 3.2 è anche possibile impostare la proprietà useRegisteredSuffixPatternMatch del bean RequestMappingHandlerMapping per mantenere il riconoscimento suffixPattern attivato ma limitato all'estensione registrata.

Qui si definisce solo JSON e XML estensioni:

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

Nota che MVC: annotazione-driven accetta ora un'opzione contentNegotiation per fornire un fagiolo personalizzato ma la proprietà di RequestMappingHandlerMapping deve essere cambiato a true (predefinito falso) (vedi https://jira.springsource.org/browse/SPR-7632).

Per questo motivo, è comunque necessario eseguire l'override di tutte le mvc: configurazione basata su annotazione.Ho aperto un biglietto per Spring per richiedere un Custom RequestMappingHandlerMapping: https://jira.springsource.org/browse/SPR-11253. Si prega di votare se si è interessati a.

Mentre si esegue l'override, fare attenzione a considerare anche l'esecuzione prioritaria della gestione Esecuzione personalizzata. In caso contrario, tutti i mapping delle eccezioni personalizzati avranno esito negativo. Si dovrà riutilizzare messageCoverters con una lista di fagioli:

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

ho implementato, nel progetto open source Resthub che io sono parte di una serie di test su questi argomenti: vedi https://github.com/resthub/resthub-spring-stack/pull/219/files e https://github.com/resthub/resthub-spring-stack/issues/217

3
//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>  
2

soluzione di configurazione basato su Java per evitare il troncamento (utilizzando una classe non-deprecato):

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:

ho capito avendo alcuni problemi con la primavera di avvio di configurazione automatica quando ho usato l'approccio di cui sopra (un po 'la configurazione automatica non ottiene efficace).

Invece, ho iniziato a utilizzare l'approccio BeanPostProcessor. Sembrava funzionare meglio.

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

4

Utilizzando la corretta classe di configurazione di 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); 
    } 
} 
2

se si è certi che il testo non corrisponderà alcuna di estensioni predefinite è possibile utilizzare sottostante Codice:

@Configuration 
@EnableWebMvc 
public class WebConfig extends WebMvcConfigurerAdapter { 

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

aggiungendo il ":. +" Ha funzionato per me, ma non fino a quando non ho rimosso ou ter parent ricci.

value = {"/username/{id:.+}"} non ha funzionato

value = "/username/{id:.+}" funziona

Speranza ho aiutato qualcuno:]

+0

questo ha risolto il mio problema, grazie. – demdem

1

La mia soluzione preferibile evitare che la Spring MVC @PathVariable per arrivare troncata è quello di aggiungere lo slash alla fine della variabile percorso.

Ad esempio:

@RequestMapping(value ="/email/{email}/") 

Quindi, la richiesta sarà simile:

http://localhost:8080/api/email/[email protected]/ 
Problemi correlati