2010-08-19 20 views
124

Tengo un controlador que proporciona acceso a la información REST:Spring MVC @PathVariable quedan truncados

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

El problema que estoy experimentando es que si golpeo el servidor con una variable de ruta con caracteres especiales que se trunca. Por ejemplo: http://localhost:8080/blah-server/blah/get/blah2010.08.19-02:25:47

El blahName parámetro será blah2010.08

Sin embargo, la llamada a request.getRequestURI() contiene toda la información que pasa en

alguna idea de cómo prevenir la primavera de truncar. el @PathVariable?

+0

Parece que esto ha sido resuelto en la primavera de 3,2-M2: ver [Permitir caminos de extensión de archivo válidos para la negociación de contenido por especificar] (https://jira.springsource.org/browse/SPR-7632) y [su documentación] (http://static.springsource.org/spring/docs/3.2.0.M2/reference/htmlsingle/# mvc-config-content-negotiation). – Arjan

Respuesta

137

Pruebe una expresión regular para el argumento @RequestMapping:

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

Gracias por la respuesta, esto me ayudó a resolver un caso en el que los nombres de usuario se recortaban de alguna manera ...(-: La otra opción con 'useDefaultSuffixPattern' no era una opción porque estamos usando clases de primavera @Configuration en lugar de XML. – evandongen

+3

Esto funciona, pero ¿cuál es el significado de los dos puntos en la expresión regular? –

+6

Noah, I have ' T usé esto en mucho tiempo, pero creo que el dos puntos separa la expresión regular del nombre del argumento para vincularlo. – earldouglas

54

Esta es, probablemente, muy relacionado con SPR-6164. En pocas palabras, el marco trata de aplicar cierta inteligencia a la interpretación de URI, eliminando lo que cree que son extensiones de archivo. Esto tendría el efecto de convertir blah2010.08.19-02:25:47 en blah2010.08, ya que cree que el .19-02:25:47 es una extensión de archivo.

Como se describe en el problema relacionado, puede deshabilitar este comportamiento declarando su propio bean DefaultAnnotationHandlerMapping en el contexto de la aplicación y estableciendo su propiedad useDefaultSuffixPattern en false. Esto anulará el comportamiento predeterminado y evitará que moleste sus datos.

+2

Activar la negociación de contenido basado en la extensión por defecto parece una opción tan extraña. ¿Cuántos sistemas realmente exponen el mismo recurso en diferentes formatos en la práctica? – Affe

+0

Intenté esto la mañana y Todavía tenía variables de ruta truncadas – phogel

+1

Esto funcionó para mí con un problema similar. Gracias, skaffman. – AHungerArtist

15

Todo después del último punto se interpreta como extensión de archivo y se corta por defecto.
En su configuración de primavera xml, puede agregar DefaultAnnotationHandlerMapping y establecer useDefaultSuffixPattern en false (el valor predeterminado es true).

tan abierto el código XML primavera mvc-config.xml (o como se le llama) y añadir

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

Ahora su @PathVariableblahName (y todos los demás, también) debe contener el nombre completo, incluyendo todos los puntos.

EDIT: Aquí está una link to the spring api

+0

No lo he intentado, pero [otros afirman] (http://stackoverflow.com/questions/4135329/how-to-change-spring-mvcs-behavior-in-handling-url-dot-character/11141184#11141184) también necesitarías eliminar '' si corresponde. – Arjan

3

Me acabo de encontrar con esto y las soluciones que aquí no suelen trabajar como lo esperaba.

Sugiero usar una expresión SpEL y múltiples asignaciones, p.

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

También me encontré con el mismo problema, y ​​establecer la propiedad en falso tampoco me ayudó. Sin embargo, the API says:

Nota que las rutas que incluyen un sufijo ".xxx" o terminan con '/' ya no serán transformados utilizando el patrón de sufijo por defecto en cualquier caso.

Intenté agregar "/ end" a mi URL RESTful, y el problema desapareció. No estoy satisfecho con la solución, pero funcionó.

Por cierto, no sé lo que pensaban los diseñadores de Spring cuando agregaron esta "función" y luego la activaron de manera predeterminada. En mi humilde opinión, debería ser eliminado.

+0

Estoy de acuerdo. Hace poco fui mordido por esto. – llambda

4

decidí por este corte

1) Alta HttpServletRequest en @PathVariable, como a continuación

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

2) Obtenga la dirección directa (En este nivel hay truncamiento) en la solicitud

request.getPathInfo() 

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

3

El problema de extensión de archivo solo existe si el parámetro está en la última parte de la URL. Cambiar

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

a

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

y todo estará bien nuevo-

3

Si usted puede editar la dirección de que las solicitudes se envían a, solución simple sería añadir una barra diagonal a ellos (y también en el valor @RequestMapping):

/path/{variable}/ 

por lo que la asignación se vería así:

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

Véase también Spring MVC @PathVariable with dot (.) is getting truncated.

27

Spring considera que cualquier cosa que esté detrás del último punto es una extensión de archivo como .json o .xml y trunque para recuperar su parámetro.

Así que si usted tiene /{blahName}:

  • /param, /param.json, /param.xml o /param.anything se traducirá en un parámetro con el valor param
  • /param.value.json, /param.value.xml o /param.value.anything se traducirá en un parámetro con el valor param.value

Si cambia su asignación a /{blahName:.+} como se sugiere, cualquier punto, incluyendo el último, se considerará como parte de su parámetro:

  • /param se traducirá en un parámetro con el valor param
  • /param.json se traducirá en un parámetro con el valor param.json
  • /param.xml resultará en un parámetro con valor param.xml
  • /param.anything resultará en un parámetro con valor param.anything
  • /param.value.json se traducirá en un parámetro con el valor param.value.json
  • ...

Si no se preocupan de reconocimiento de extensión, se puede desactivar anulando 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> 

Por lo tanto, de nuevo, si usted tiene /{blahName}:

  • /param,.210, /param.xml o /param.anything se traducirá en un parámetro con el valor param
  • /param.value.json, /param.value.xml o /param.value.anything se traducirá en un parámetro con el valor param.value

Nota: la diferencia de la configuración por defecto es visible sólo si tiene una mapeo como /something.{blahName}. Ver Resthub project issue.

Si desea mantener la administración de extensiones, desde Spring 3.2 también puede establecer la propiedad useRegisteredSuffixPatternMatch de bean RequestMappingHandlerMapping para mantener activado el reconocimiento sufijoPattern pero limitado a la extensión registrada.

Aquí se define sólo JSON y XML extensiones:

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

Tenga en cuenta que mvc: anotación impulsada acepta ahora una opción contentNegotiation para proporcionar un grano de costumbre, pero la propiedad de RequestMappingHandlerMapping tiene que ser cambiado a true (predeterminado falso) (compárese con https://jira.springsource.org/browse/SPR-7632).

Por ese motivo, aún debe sobrescribir toda la configuración de mvc: anotación.Abrí un boleto a Spring para solicitar un CustomMappingHandlerMapping personalizado: https://jira.springsource.org/browse/SPR-11253. Por favor, vote si le interesa.

Al anular, tenga en cuenta también la anulación de gestión de ejecución personalizada. De lo contrario, todas las asignaciones de excepciones personalizadas fallarán. Tendrá que volver a utilizar messageCoverters con una lista de frijol:

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

he implementado, en el proyecto de código abierto Resthub que soy parte de un conjunto de pruebas sobre estos temas: ver https://github.com/resthub/resthub-spring-stack/pull/219/files y 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

solución de configuración basada en Java para evitar el truncamiento (usando una clase no desaprobado-):

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

ACTUALIZACIÓN:

me di cuenta de que tiene algunos problemas con la primavera de arranque de configuración automática cuando utilicé el enfoque anterior (algunos de configuración automática no consigue efectiva).

En su lugar, comencé a utilizar el enfoque BeanPostProcessor. Parecía estar funcionando mejor.

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

Utilización de la clase de configuración de Java correcta:

@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

si está seguro de que su texto no coincide con ninguna de las extensiones por defecto se puede utilizar a continuación código:

@Configuration 
@EnableWebMvc 
public class WebConfig extends WebMvcConfigurerAdapter { 

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

agregar el ":. +" Funcionó para mí, pero no hasta que lo eliminé ter soportes rizados.

value = {"/username/{id:.+}"} no funcionó

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

Espero ayudado a alguien:]

+0

esto resolvió mi problema, gracias. – demdem

1

Mi solución preferible prevenir el Spring MVC @PathVariable para obtener truncada es añadir una barra final en el extremo de la variable de ruta.

Por ejemplo:

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

Por lo tanto, la solicitud se verá así:

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