Spring MVC @PathVariable получает усеченный

У меня есть контроллер, который предоставляет RESTful доступ к информации:

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

Проблема, с которой я столкнулась, заключается в том, что если я попал на сервер с переменной пути со специальными символами, она усекается. Например: http://localhost:8080/blah-server/blah/get/blah2010.08.19-02:25:47

Параметр blahName будет blah2010.08

Однако вызов request.getRequestURI() содержит всю информацию, переданную в.

Любая идея, как предотвратить Spring от обрезания @PathVariable?

Ответ 1

Попробуйте регулярное выражение для аргумента @RequestMapping:

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

Ответ 2

Это, вероятно, тесно связано с SPR-6164. Вкратце, структура пытается применить некоторые умственные способности к интерпретации URI, удалив то, что, по его мнению, является расширением файлов. Это привело бы к превращению blah2010.08.19-02:25:47 в blah2010.08, поскольку он считает, что .19-02:25:47 является расширением файла.

Как описано в связанной проблеме, вы можете отключить это поведение, объявив свой собственный DefaultAnnotationHandlerMapping bean в контексте приложения и установив для свойства useDefaultSuffixPattern значение false. Это приведет к переопределению поведения по умолчанию и прекратит его приставать к вашим данным.

Ответ 3

Spring считает, что все, что находится за последней точкой, является расширением файла, например .json или .xml, и обрезает его для получения вашего параметра.

Итак, если у вас есть /{blahName}:

  • /param, /param.json, /param.xml или /param.anything приведет к параметру со значением param
  • /param.value.json, /param.value.xml или /param.value.anything приведет к параметру со значением param.value

Если вы измените свое сопоставление на /{blahName:.+}, как было предложено, любая точка, включая последнюю, будет считаться частью вашего параметра:

  • /param приведет к параметру со значением param
  • /param.json приведет к параметру со значением param.json
  • /param.xml приведет к параметру со значением param.xml
  • /param.anything приведет к параметру со значением param.anything
  • /param.value.json приведет к параметру со значением param.value.json
  • ...

Если вы не любите распознавание расширений, вы можете отключить его, переопределив 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>

Итак, если у вас есть /{blahName}:

  • /param, /param.json, /param.xml или /param.anything приведет к параметру со значением param
  • /param.value.json, /param.value.xml или /param.value.anything приведет к параметру со значением param.value

Примечание: отличие от конфигурации по умолчанию отображается только в том случае, если у вас есть сопоставление типа /something.{blahName}. См. Проблема проекта Resthub.

Если вы хотите сохранить управление добавлением, так как Spring 3.2 вы также можете установить свойство useRegisteredSuffixPatternMatch для RequestMappingHandlerMapping bean, чтобы поддерживать распознавание suffixPattern, но ограничивается зарегистрированным расширением.

Здесь вы определяете только расширения json и 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>

Обратите внимание, что mvc: annotation-driven теперь принимает параметр contentNegotiation для предоставления пользовательского bean, но свойство RequestMappingHandlerMapping должно быть изменено на true (по умолчанию false) (cf. https://jira.springsource.org/browse/SPR-7632).

По этой причине вам все равно придется переопределить всю конфигурацию mvc: annotation. Я открыл билет на Spring, чтобы запросить пользовательскую RequestMappingHandlerMapping: https://jira.springsource.org/browse/SPR-11253. Пожалуйста, проголосуйте, если вы заинтересованы.

В то время как переопределение, будьте осторожны, чтобы рассмотреть также настраиваемое управление выполнением Execution. В противном случае все ваши пользовательские сопоставления исключений не удастся. Вам придется повторно использовать messageCoverters со списком 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>

Я реализовал в проекте с открытым исходным кодом Resthub, что я являюсь частью набора тестов по этим темам: см. https://github.com/resthub/resthub-spring-stack/pull/219/files и https://github.com/resthub/resthub-spring-stack/issues/217

Ответ 4

Все после последней точки интерпретируется как расширение файла и отключено по умолчанию.
В вашем spring config xml вы можете добавить DefaultAnnotationHandlerMapping и установить useDefaultSuffixPattern в false (по умолчанию - true).

Итак, откройте свой spring xml mvc-config.xml (или, тем не менее, он вызывается) и добавьте

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

Теперь ваш @PathVariable blahName (и все остальные тоже) должен содержать полное имя, включая все точки.

EDIT: ссылка на spring api

Ответ 5

Я также столкнулся с одной и той же проблемой, и установка свойства false также не помогла мне. Однако API говорит:

Обратите внимание, что пути, которые включают суффикс ".xxx", или конец с "/" уже не будет преобразовываться с использованием шаблона суффикса по умолчанию в любом случае.

Я попытался добавить "/end" к моему URL RESTful, и проблема исчезла. Я не рад решению, но это действительно сработало.

Кстати, я не знаю, о чем думали дизайнеры Spring, добавив эту "функцию", а затем включил ее по умолчанию. ИМХО, его следует удалить.

Ответ 6

Я разрешил этот хак

1) Добавлен HttpServletRequest в @PathVariable, как показано ниже

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

2) Получите URL-адрес напрямую (на этом уровне нет усечения) в запросе

request.getPathInfo() 

Spring MVC @PathVariable с точкой (.) усекается

Ответ 7

Использование правильного класса конфигурации 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);
    }
}

Ответ 8

Я просто столкнулся с этим, и решения здесь вообще не работали, как я ожидал.

Я предлагаю использовать выражение SpEL и несколько сопоставлений, например

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

Ответ 9

Проблема с расширением файла существует только в том случае, если параметр находится в последней части URL-адреса. Изменить

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

to

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

и все будет хорошо снова -

Ответ 10

Если вы можете редактировать адрес, на который отправляются запросы, простым исправлением было бы добавить к ним конечную косую черту (а также значение @RequestMapping):

/path/{variable}/

поэтому отображение будет выглядеть так:

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

См. также Spring MVC @PathVariable с точкой (.) усекается.

Ответ 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

добавление ":. +" работало для меня, но только после того, как я удалил внешние фигурные скобки.

value = {"/username/{id:.+}"} не работает

value = "/username/{id:.+}" работает

Надеюсь, я помог кому-то:]

Ответ 13

Решение по настройке на основе Java для предотвращения усечения (с использованием класса, не устаревшего):

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

Источник: http://www.javacodegeeks.com/2013/01/spring-mvc-customizing-requestmappinghandlermapping.html

UPDATE:

Я понял, что у меня возникли проблемы с Spring автоматической настройкой загрузки, когда я использовал описанный выше подход (некоторая автоматическая настройка не работает).

Вместо этого я начал использовать подход BeanPostProcessor. Казалось, что он работает лучше.

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);
    }
}

Вдохновленный из: http://ronaldxq.blogspot.com/2014/10/spring-mvc-setting-alwaysusefullpath-on.html

Ответ 14

если вы уверены, что ваш текст не будет соответствовать ни одному из расширений по умолчанию, вы можете использовать ниже код:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

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

Ответ 15

Мое предпочтительное решение для предотвращения обрезания Spring MVC @PathVariable заключается в добавлении конечной косой черты в конце переменной пути.

Например:

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

Итак, запрос будет выглядеть так:

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