Весна MVC @PathVariable стає усіченою


142

У мене є контролер, який забезпечує швидкий доступ до інформації:

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


Відповіді:


149

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

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

1
Дякую за відповідь, це допомогло мені вирішити випадок, коли імена користувачів якось обрізали .. (-: Інший варіант із "useDefaultSuffixPattern" не був варіантом, оскільки ми використовуємо весняні класи @Configuration замість XML.
evandongen

3
Це працює, але яке значення товстої кишки в регексе?
Ной Ітер

6
Ное, я давно не використовував це, але думаю, що двокрапка відокремлює регулярний вираз від назви аргументу, щоб прив’язати його.
Earldouglas

3
у нас був аналогічний problam /item/user@abc.com, все, що після @ було урізано, це було вирішено, додавши ще одну косу рису /item/user@abc.com/
Titi Wangsa bin Damhore

59

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

Як описано в пов'язаній проблемі, ви можете відключити цю поведінку, оголосивши власний DefaultAnnotationHandlerMappingбоб у контексті програми та встановивши його useDefaultSuffixPatternвластивість false. Це скасує поведінку за замовчуванням і зупинить її обробку даних.


3
Увімкнення переговорів щодо вмісту на основі розширення за замовчуванням виглядає таким дивним вибором. Скільки систем реально виставляє один і той же ресурс у різних форматах на практиці?
Affe

Я спробував це вранці і досі мав усічені змінні шляху.
фогель

30
+1 за чудову відповідь, а також за використання фрази "домогтися ваших даних"
Кріс Томпсон

11
Для користувачів Spring 3.1 - якщо ви використовуєте нове RequestMappingHandlerMappingзамість цього, властивість встановлювати useSuffixPatternMatch(також до false). @ Ted: пов'язаний випуск згадує, що в 3.2 вони сподіваються додати трохи більше контролю, так що це не повинно бути абсолютно чи нічим.
Нік

2
Навесні 4.2 це налаштувати трохи простіше. Ми використовуємо класи конфігурації Java і розширюємо той, WebMvcConfigurationSupportщо забезпечує простий гак: public void configurePathMatch(PathMatchConfigurer configurer)- просто перекрийте це і налаштуйте шлях, який відповідає вашому бажанню.
pmckeown

31

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автоматичне:

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

Якщо ви хочете зберегти управління розширеннями, починаючи з весни 3.2, ви також можете встановити властивість useRegisteredSuffixPatternMatch ReanMappingHandlerMapping, щоб зберегти розпізнавання суфікса активізованим, але обмеженим зареєстрованим розширенням.

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

З цієї причини вам все одно доведеться перекрити всю конфігурацію mvc: анотації. Я відкрив квиток на Весну, щоб попросити власну 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/isissue/217


16

Все після останньої крапки інтерпретується як розширення файлу та відрізається за замовчуванням.
У своєму весняному конфігурації xml ви можете додати DefaultAnnotationHandlerMappingта встановити useDefaultSuffixPatternзначення false(за замовчуванням є true).

Тож відкрийте свій весняний xml mvc-config.xml(або, як це називається) і додайте

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

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

EDIT: Ось посилання на весняний api


Я не пробував, але інші заявляють , що тоді вам також потрібно буде видалити, <mvc:annotation-driven />якщо потрібно.
Ар'ян

7

Я також зіткнувся з тією ж проблемою, і встановлення властивості "false" також не допомогло мені. Однак API говорить :

Зауважте, що шляхи, які включають суфікс ".xxx" або закінчуються на "/", не будуть перетворені, використовуючи шаблон суфікса за замовчуванням у будь-якому випадку.

Я спробував додати "/ end" до своєї RESTful URL-адреси, і проблема усунулася. Я не задоволений рішенням, але воно спрацювало.

До речі, я не знаю, що думали дизайнери весни, коли вони додали цю "функцію", а потім увімкнули її за замовчуванням. ІМХО, його слід видалити.


Я згоден. Мене нещодавно трохи покусало.
llambda

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

Це спрацювало для мене чудово. Працює на Tomcat Spring версія 4.3.14
Дейв

4

Я вирішився цим хаком

1) Додано HttpServletRequest в @PathVariable, як показано нижче

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

2) Отримайте URL-адресу безпосередньо (на цьому рівні немає усікання) у запиті

request.getPathInfo() 

Весна MVC @PathVariable з крапкою (.) Стає усіченою


3

Я просто натрапив на це, і рішення тут, як правило, не спрацювали так, як я очікував.

Я пропоную використовувати вираз SpEL та кілька відображень, наприклад

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

3

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

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

до

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

і все знову буде добре-


3

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

/path/{variable}/

щоб відображення виглядало так:

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

Дивіться також Spring MVC @PathVariable з крапкою (.) Стає усіченою .


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>       

3

додавання ":. +" працювало на мене, але не поки я не зняв зовнішні фігурні дужки.

value = {"/username/{id:.+}"} не працювало

value = "/username/{id:.+}" працює

Сподіваюся, я комусь допоміг:]


2

Конфігураційне рішення на базі 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


2

якщо ви впевнені, що ваш текст не відповідає жодному з розширень за замовчуванням, ви можете використовувати нижче код:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

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

1

Моє переважне рішення для запобігання урізанню Spring MVC @PathVariable - це додавання останньої косої риски в кінці змінної шляху.

Наприклад:

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

Отже, запит буде виглядати так:

http://localhost:8080/api/email/test@test.com/

1

Проблема , з якою ви зіткнулися з - за весни інтерпретації останньої частини Урі після з точки (.) В якості розширення файлу , як .json або .xml. Отже, коли весна намагається вирішити змінну шляху, вона просто скорочує решту даних після того, як вона зустріне крапку (.) В кінці урі. Примітка: також це відбувається лише в тому випадку, якщо ви тримаєте змінну шляху в кінці ури.

Наприклад, розгляньте 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 = "посилання", останній біт після. стає усіченим, коли змінна шлях трактується.

Отже, запобігти цьому є два можливі способи:

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) Перезаписавши налаштування webmvc за замовчуванням Spring

Spring пропонує способи змінити конфігурації за замовчуванням, які імпортуються за допомогою приміток @EnableWebMvc. Ми можемо налаштувати конфігурацію Spring MVC, оголосивши власний бон DefaultAnnotationHandlerMapping у контексті програми та встановивши його властивість 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.