如何防止参数绑定解释Spring 3.0.5中的逗号?

时间:2011-02-15 00:03:39

标签: java spring data-binding spring-mvc

考虑以下控制器方法:

@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|
  6. 示例3是问题。我希望(想要/需要)输出fq = foo,bar

    我尝试使用\并使用%3C转义逗号,但是没有工作。

    如果我查看HttpServletRequest对象的版本:

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

    打印预期输出:fqs = foo,bar。所以“问题”在于Spring数据绑定。

    我可以绕过Spring的绑定并使用HttpServletRequest但我真的不想,因为我在我的实际代码中使用支持bean (同样的事情正在发生)并且不希望重新实现绑定功能。我希望有人可以提供一种通过转义或其他机制来防止这种行为的简单方法。

    TIA

    更新:我在推特上发布了这个Q并得到了一个回复说预期输出出现在Spring 3.0.4.RELEASE 。我现在已经证实了这种情况,因此是一个临时修复。我将继续将此作为Spring JIRA系统上的错误记录下来。如果有人可以提供解决方案或修复3.0.5,我会接受他们的回答。

7 个答案:

答案 0 :(得分:31)

我已经测试了你的代码:它令人难以置信,但我无法重现你的问题。我已经下载了最新版本的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。 我的代码与你的代码有什么不同?难道我做错了什么? 我想帮助你,所以如果你有任何疑问,或者我可以为你做一些其他测试,那将是一种乐趣。

更新/解决方案
用你的代码我再现了这个问题;您的调度程序servlet配置中有标记<mvc:annotation-driven />,因此您静默使用默认转换服务FormattingConversionService的实例,其中包含从StringString[]的默认转换器使用逗号作为分隔符。 您必须使用包含自己的转换器的其他转换服务bean,从StringString[]。您应该使用不同的分隔符,我选择使用“;”因为它是常用于查询字符串的分隔符(“?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, ";");
    }
}

然后,您必须在配置中指定此转换服务bean:

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

问题已解决,现在您应检查是否有任何副作用。我希望您在应用程序中不需要从StringString[]的原始转换(使用逗号作为分隔符)。 ;-)

答案 1 :(得分:21)

我找到了最优雅,最短的方式 - 将@InitBinder添加到@Controller

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

它将String转换为String [],而不使用分隔符(null param),使用Spring类org.springframework.beans.propertyeditors.StringArrayPropertyEditor。 如果同一项目中的某个人将使用新的默认转换方式,那就没问题了。

答案 2 :(得分:4)

正如菲利普波特所建议的那样,我将问题的“更新”作为答案发布,因为它可能很容易错过...

从Spring 3.0.5.RELEASE降级到3.0.4.RELEASE 修复这个问题,当使用@RequestParam注释时,表明它是3.0.5的错误。

但是,当绑定到表单支持bean时,它不会解决相关问题 - 这就是我在webapp中所拥有的。我已经将所有版本测试回3.0.0.RELEASE并获得相同的结果(/test?fq=foo,bar生成fq = foo|bar)。

E.g。

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

其中SearchRequestParams包含字段String[] fq

如果有人对此有所解决,我很乐意接受他们的回答。

答案 3 :(得分:3)

javanna已经指出了正确的根本原因。我只是想进一步指出你也可以完全删除StringToArrayConverter as shown herehere

答案 4 :(得分:0)

它是一个黑客,但是,你考虑过你的params用' - '

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

或者任何其他分隔符可能是〜,或!,或^,或???

答案 5 :(得分:0)

以下是我在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;
            }
        };
    }
}

答案 6 :(得分:0)

rougou 的评论是唯一对我有用的解决方案,因为我使用的是 List<String> 而不是 String[]

将此添加到您的控制器以消除解析逗号分隔的值:

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(List.class, new CustomCollectionEditor(List.class));
    }