it-swarm-ru.tech

Как запретить привязке параметров интерпретировать запятые в Spring 3.0.5?

Рассмотрим следующий метод контроллера:

@RequestMapping(value = "/test", method = RequestMethod.GET)
public void test(@RequestParam(value = "fq", required = false) String[] filterQuery) {
    logger.debug(fq = " + StringUtils.join(filterQuery, "|"));
}

Вот вывод для различных комбинаций fq:

  1. /test?fq=foo приводит к fq = foo
  2. /test?fq=foo&fq=bar приводит к fq = foo|bar
  3. /test?fq=foo,bar приводит к fq = foo|bar
  4. /test?fq=foo,bar&fq=bash приводит к fq = foo,bar|bash
  5. /test?fq=foo,bar&fq= приводит к fq = foo,bar|

Пример 3 - это проблема. Я ожидаю (хочу/нужно), чтобы он вывел fq = foo,bar.

Я пытался экранировать запятую с помощью \ и использовать %3C, но это не помогло.

Если я посмотрю на версию объекта HttpServletRequest:

String[] fqs = request.getParameterValues("fq");
logger.debug(fqs = " + StringUtils.join(fqs, "|"));

Он печатает ожидаемый результат: fqs = foo,bar. Таким образом, "проблема" связана с привязкой данных Spring.

Я мог бы обойти привязку Spring и использовать HttpServletRequest, но я действительно не хочу этого, поскольку я использую backan bean в моем реальном коде (происходит то же самое) и не хочу повторно -Определить функциональность привязки. Я надеюсь, что кто-то может предоставить простой способ предотвращения этого поведения через экранирование или какой-либо другой механизм.

ТИА

ОБНОВЛЕНИЕ: Я опубликовал этот вопрос в Твиттере и получил ответ о том, что ожидаемый результат появится в Spring 3.0.4.RELEASE. Теперь я подтвердил, что это так, и поэтому это временное исправление. Я продолжу и буду регистрировать это как ошибку в системе Spring JIRA. Если кто-то может предоставить решение или исправить с 3.0.5, я приму их ответ.

50
nickdos

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

package test;

import org.Apache.commons.lang.StringUtils;
import org.Apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/test/**")
public class MyController {

    private static final Logger logger = Logger.getLogger(MyController.class);

    @RequestMapping(value = "/test/params", method = RequestMethod.GET)
    public void test(SearchRequestParams requestParams, BindingResult result) {
    logger.debug("fq = " + StringUtils.join(requestParams.getFq(), "|"));
    }
}

это мой класс SearchRequestParams:

package test;

public class SearchRequestParams {
    private String[] fq;

    public String[] getFq() {
    return fq;
    }

    public void setFq(String[] fq) {
    this.fq = fq;
    }
}

и это моя простая конфигурация пружины:

<bean id="urlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />

<bean class="test.MyController" />

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix">
        <value>/WEB-INF/jsp/</value>
    </property>
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>

Я проверил свой код в Tomcat 7.0.8; когда я набираю http://localhost:8080/testweb/test/params.htm?fq=foo,bar, я могу прочитать в моем файле журнала следующую строку: DEBUG fq = foo,bar. Чем отличается мой код от вашего? Я делаю что-то неправильно? Я хотел бы помочь вам, так что если у вас есть какие-либо сомнения или если я смогу сделать какие-то другие тесты для вас, это будет приятно.

ОБНОВЛЕНИЕ/РЕШЕНИЕ
С вашим кодом я воспроизвел проблему; у вас есть тег <mvc:annotation-driven /> в конфигурации вашего сервлет-диспетчера, поэтому вы молча используете службу преобразования по умолчанию, экземпляр FormattingConversionService, который содержит конвертер по умолчанию из String в String[], который использует запятую в качестве разделителя. Вы должны использовать другой компонент службы преобразования, содержащий ваш собственный преобразователь из String в String[]. Вы должны использовать другой разделитель, я решил использовать ";" потому что этот разделитель обычно используется в строке запроса ("? first = 1; second = 2; third = 3"):

import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;

public class CustomStringToArrayConverter implements Converter<String, String[]>{
   @Override
    public String[] convert(String source) {
        return StringUtils.delimitedListToStringArray(source, ";");
    }
}

Затем вы должны указать этот компонент службы преобразования в вашей конфигурации:

<mvc:annotation-driven conversion-service="conversionService" />

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="au.org.ala.testspringbinding.CustomStringToArrayConverter" />
        </list>
    </property>
</bean>

Проблема исправлена, теперь вы должны проверить наличие побочных эффектов. Я надеюсь, что вам не нужно исходное преобразование из String в String[] (с запятой в качестве разделителя). ;-)

31
javanna

Я нашел самый элегантный и самый короткий способ для меня - добавить @InitBinder к @Controller:

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor(null));
}

Он преобразует строку в строку [] без использования разделителя (параметр null) с классом Spring org.springframework.beans.propertyeditors.StringArrayPropertyEditor. Если кто-то в том же проекте будет использовать новый способ конвертации по умолчанию, все будет в порядке.

20
TachikomaGT

По предложению Филиппа Поттера, я публикую "обновление" на мой вопрос в качестве ответа, так как его легко было пропустить ...

Снижение с Spring 3.0.5.RELEASE до 3.0.4.RELEASE исправлено проблема при использовании аннотации @RequestParam, предполагающая, что это ошибка в 3.0.5.

Однако он НЕ исправляет связанную проблему при привязке к компоненту, поддерживающему форму - это то, что у меня есть в моем веб-приложении. Я протестировал все версии обратно до 3.0.0.RELEASE и получил тот же результат (/test?fq=foo,bar выдает fq = foo|bar).

Например.

@RequestMapping(value = "/test", method = RequestMethod.GET)
public void test(SearchRequestParams requestParams, BindingResult result) {
    logger.debug("fq = " + StringUtils.join(requestParams.getFq(), "|"));
}

где SearchRequestParams содержит поле String[] fq.

Если у кого-то есть решение, я с радостью приму их ответ.

4
nickdos

джаванна уже указала правильную причину. Я просто хотел еще раз подчеркнуть, что вы также можете полностью удалить StringToArrayConverter как показано здесь и здесь .

3
Rossen Stoyanchev

Следующее - мой путь обхода запятой в запросе MVC

Это мое понимание эволюции Springs решений такого типа: документация очень расплывчатая о том, что является новейшим решением.

Сначала был WebMvcConfigAdapter, реализующий интерфейс WebMvcConfig. Это в конечном итоге устарело

Это было заменено WebMvcConfigSupport. Это в конечном итоге устарело, но это было лучше, чем первое решение. Основная проблема заключалась в том, что он отключил автоконфигурацию MVC и имел побочные проблемы, такие как swagger.html, не работающий и информация о приводе отсутствовала в хорошем формате, а дата стала большой десятичной

Последним является переработанный интерфейс WebMvcConfig, который реализует методы по умолчанию с использованием функций Java 8.

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

Это позволяет вам изменить то, что вам нужно, в нашем случае пользовательский преобразователь, не влияя ни на что другое или не переопределяя другой, чтобы заставить чванство работать, или иметь дело с выводом информации привода

Ниже приведены два класса, которые реализовали мое изменение, чтобы обойти разделение запятыми строк в списке при обработке запроса MVC:
Это все еще производит список строк, но только с одним значением.

import Java.util.Collection;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfigUUID implements WebMvcConfigurer {
  @Override
  public void addFormatters(FormatterRegistry registry)  {
    registry.removeConvertible(String.class,Collection.class);
    registry.addConverter(String.class,Collection.class,BypassCommaDelimiterConfiguration.commaDelimiterBypassedParsingConverter());
  }
}

import Java.util.ArrayList;
import Java.util.List;
import org.springframework.core.convert.converter.Converter;

public class BypassCommaDelimiterConfiguration  {
    public static Converter<String, List<String>> commaDelimiterBypassedParsingConverter() {
        return new Converter<String, List<String>>() {
            @Override
            public List<String> convert(final String source) {
                final List<String> classes = new ArrayList<String>();
                classes.add(source);
                return classes;
            }
        };
    }
}
0
Joseph Rogers

Это хак, но вы рассматривали возможность передачи своих параметров, разделенных '-'

/test?fq=foo-bar results in fq = foo-bar
/test?fq=foo-bar&fq=bash results in fq = foo-bar|bash 

Или любой другой разделитель, может быть ~, или!, Или ^, или ???

0
Sumit