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?


person phogel    schedule 19.08.2010    source источник
comment
Кажется, это было решено в Spring 3.2-M2: см. Разрешить допустимые пути расширения файлов для согласования содержимого указать и его документация.   -  person Arjan    schedule 17.10.2012


Ответы (15)


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

RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName:.+}")
person earldouglas    schedule 19.08.2010
comment
Спасибо за ответ, это помогло мне решить случай, когда имена пользователей каким-то образом были обрезаны .. (-: Другой вариант с 'useDefaultSuffixPattern' не подходил, потому что мы используем классы пружины @Configuration вместо XML. - person evandongen; 20.10.2011
comment
Это работает, но каково значение двоеточия в регулярном выражении? - person Noah Yetter; 02.11.2012
comment
Ной, я давно этим не пользовался, но думаю, двоеточие отделяет регулярное выражение от имени аргумента, к которому его нужно привязать. - person earldouglas; 02.11.2012
comment
у нас была аналогичная проблема /item/[email protected], все, что было после усечения @, было решено добавлением еще одной косой черты /item/[email protected]/ - person Titi Wangsa bin Damhore; 03.12.2014

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

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

person skaffman    schedule 19.08.2010
comment
Включение согласования содержимого на основе расширений по умолчанию кажется таким странным выбором. Сколько систем действительно предоставляют один и тот же ресурс в разных форматах на практике? - person Affe; 20.08.2010
comment
Я попробовал это утром, и все еще оставались усеченные переменные пути. - person phogel; 20.08.2010
comment
+1 за отличный ответ, а также за использование фразы о приставании к вашим данным - person Chris Thompson; 14.11.2010
comment
это может быть не лучшим решением, если вы используете суффикс .xml / .json на страницах и ContentNegotiatingViewResolver - person Ted Pennings; 13.04.2011
comment
Для пользователей Spring 3.1 - если вместо этого вы используете новый RequestMappingHandlerMapping, устанавливаемое свойство - useSuffixPatternMatch (также false). @Ted: в связанной проблеме упоминается, что в 3.2 они надеются добавить немного больше контроля, чтобы не было все или ничего. - person Nick; 20.01.2012
comment
В Spring 4.2 это немного проще настроить. Мы используем классы конфигурации Java и расширяем WebMvcConfigurationSupport, который обеспечивает простую ловушку: public void configurePathMatch(PathMatchConfigurer configurer) - просто переопределите это и настройте соответствие пути, как вам нравится. - person pmckeown; 28.03.2016

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 bean-объекта RequestMappingHandlerMapping, чтобы поддерживать распознавание суффиксPattern активным, но ограниченным зарегистрированным расширением.

Здесь вы определяете только расширения 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: с управлением аннотациями теперь принимает параметр contentNegotiation для предоставления настраиваемого bean-компонента, но свойство RequestMappingHandlerMapping должно быть изменено на true (по умолчанию false) (см. https://jira.springsource.org/browse/SPR-7632).

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

При переопределении не забывайте учитывать также переопределение настраиваемого управления выполнением. В противном случае все ваши настраиваемые сопоставления исключений завершатся ошибкой. Вам придется повторно использовать messageCoverters со списковым компонентом:

<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

person bmeurant    schedule 23.12.2013

Все, что находится после последней точки, интерпретируется как расширение файла и по умолчанию обрезается.
В XML-файле конфигурации spring вы можете добавить 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 (и все остальные тоже) должен содержать полное имя, включая все точки.

РЕДАКТИРОВАТЬ: вот ссылка на весенний API

person Jan    schedule 22.10.2010
comment
Я не пробовал, но другие утверждают, что вам также необходимо удалить <mvc:annotation-driven />, если применимо. - person Arjan; 17.10.2012

Используя правильный класс конфигурации 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);
    }
}
person jebeaudet    schedule 05.03.2015
comment
Это отлично сработало для меня. Работает на Tomcat Spring версии 4.3.14 - person Dave; 15.08.2018

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

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

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

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

person Steve11235    schedule 06.04.2012
comment
Я согласен. Меня недавно это укусило. - person lammy; 10.09.2014

Я решил этим взломом

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

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

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

request.getPathInfo() 

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

person Kanagavelu Sugumar    schedule 26.02.2013

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

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

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

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

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

to

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

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

person chrismarx    schedule 12.07.2013

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

/path/{variable}/

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

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

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

person Michał Rybak    schedule 28.11.2013

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

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

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

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

person Martin Čejka    schedule 15.01.2016

Решение для конфигурации на основе 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

ОБНОВЛЕНИЕ:

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

Вместо этого я начал использовать подход 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.

person burcakulug    schedule 04.03.2015

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

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseRegisteredSuffixPatternMatch(true);
    }
}
person Bassem Reda Zohdy    schedule 29.09.2015

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

Например:

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

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

http://localhost:8080/api/email/[email protected]/
person Johnny    schedule 07.02.2017

Проблема, с которой вы столкнулись, связана с тем, что spring интерпретирует последнюю часть uri после точки (.) как расширение файла, например .json или .xml. Поэтому, когда Spring пытается разрешить переменную пути, он просто обрезает остальные данные после того, как встречает точку (.) В конце uri. Примечание: это также происходит, только если вы сохраняете переменную пути в конце uri.

Например, рассмотрим 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) {
        // ...  
    }
}

В указанном выше URL firstValue = "gallery.df" и secondValue = "link" последний бит после. усекается при интерпретации переменной пути.

Итак, предотвратить это можно двумя способами:

1.) Использование сопоставления регулярного выражения

Используйте регулярное выражение в конце сопоставления

@GetMapping("/example/{firstValue}/{secondValue:.+}")   
public void example(
  @PathVariable("firstValue") String firstValue,
  @PathVariable("secondValue") String secondValue) {
    //...
}

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

2.) Добавление косой черты в конце @PathVariable

@GetMapping("/example/{firstValue}/{secondValue}/")
public void example(
  @PathVariable("firstValue") String firstValue,
  @PathVariable("secondValue") String secondValue) {
    //...
}

Это закроет нашу вторую переменную, защищающую ее от поведения Spring по умолчанию.

3) Путем переопределения конфигурации Spring по умолчанию webmvc

Spring предоставляет способы переопределить конфигурации по умолчанию, которые импортируются с помощью аннотаций @EnableWebMvc. Мы можем настроить конфигурацию Spring MVC, объявив нашу собственную DefaultAnnotationHandlerMapping bean в контексте приложения и присвоив его свойству useDefaultSuffixPattern значение false. Пример:

@Configuration
public class CustomWebConfiguration extends WebMvcConfigurationSupport {

    @Bean
    public RequestMappingHandlerMapping 
      requestMappingHandlerMapping() {

        RequestMappingHandlerMapping handlerMapping
          = super.requestMappingHandlerMapping();
        handlerMapping.setUseSuffixPatternMatch(false);
        return handlerMapping;
    }
}

Имейте в виду, что переопределение этой конфигурации по умолчанию влияет на все URL-адреса.

Примечание. здесь мы расширяем класс WebMvcConfigurationSupport, чтобы переопределить методы по умолчанию. Есть еще один способ переопределить конфигурации по умолчанию, реализовав интерфейс WebMvcConfigurer. Подробнее об этом читайте: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html

person Ananthapadmanabhan    schedule 03.04.2019