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

Posted

技术标签:

【中文标题】Spring 3.0.5中如何防止参数绑定解释逗号?【英文标题】:How to prevent parameter binding from interpreting commas in Spring 3.0.5? 【发布时间】:2011-06-27 07:00:23 【问题描述】:

考虑以下控制器方法:

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

这是不同fq 组合的输出:

    /test?fq=foo 导致 fq = foo /test?fq=foo&fq=bar 导致 fq = foo|bar /test?fq=foo,bar 导致 fq = foo|bar /test?fq=foo,bar&fq=bash 导致 fq = foo,bar|bash /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,但我真的不想这样做,因为我在我的真实代码中使用了 backing bean(同样的事情正在发生)并且不要不希望重新实现绑定功能。我希望有人可以提供一种通过转义或其他机制来防止这种行为的简单方法。

TIA

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

【问题讨论】:

记录为错误:jira.springsource.org/browse/SPR-7963 我建议您将您的解决方案添加为您自己问题的答案,以便其他人更清楚您找到了解决方案。 感谢菲利普的建议。事实证明,使用 3.0.4 的修复适用于 @RequestMapping,但在绑定到表单支持 bean 时不能修复相同的问题。所以我还没有修复我的应用程序。我还没有收到关于 Spring Jira 问题的评论/更新——我认为这有点松懈。 【参考方案1】:

按照 Philip Potter 的建议,我将发布问题的“更新”作为答案,因为它可能很容易被忽略...

从 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)。

例如

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

其中SearchRequestParams 包含一个字段String[] fq

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

【讨论】:

后代更新:Javanna 在上面接受的答案中正确识别了问题。【参考方案2】:

这是一个 hack,但是,您是否考虑过传递以“-”分隔的参数

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

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

【讨论】:

参数值来自 Solr 方面 - 虽然我可以在索引时使用它们来用 something_else 替换逗号,但对于我同意的黑客来说,这需要付出太多额外的努力。跨度> 【参考方案3】:

我已经测试了您的代码:这令人难以置信,但我无法重现您的问题。我已经下载了最新版本的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 配置中有标签 &lt;mvc:annotation-driven /&gt;,因此您静默使用默认转换服务,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[] 的原始转换(以逗号作为分隔符)。 ;-)

【讨论】:

谢谢。您粘贴的代码基本上就是我正在使用的。我的 webapp 中仍然有控制器方法,我只是再次尝试,仍然在我的版本中看到“foo|bar”。我现在想知道我是否遇到了其中一个奇怪的 Maven 依赖问题,因为我依赖于使用旧版本 Spring 的项目。我将创建一个全新的“裸”Spring 项目并尝试并报告。 我刚刚创建了一个新的 Maven webapp (JEE 6),支持 Spring MVC 3.0.5(使用 Netbeans)。我稍微修改了 pom 并根据这个答案创建了两个类。在 Jetty 和 tomcat(6.0.20)中运行 webapp,我仍然得到输出 fq = foo|bar(我现在有输出到浏览器)。我已经把代码放在谷歌代码上:svn checkout ala-hubs.googlecode.com/svn/trunk/testSpringBinding - 很想深入了解这个...... 嗯,很高兴为您提供帮助,让我看看您的代码。我会在几个小时后发布一些新闻。 很棒的研究和工作——这个答案并不完全适合我的项目,但我不得不说这个问题和答案都很棒。 与您发布的 XML 等效的 Java 代码是什么?【参考方案4】:

javanna 已经指出了正确的根本原因。我只是想进一步指出,您还可以完全删除 StringToArrayConverter as shown here 和 here。

【讨论】:

【参考方案5】:

我找到了对我来说最优雅和最短的方法 - 将 @InitBinder 添加到 @Controller

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

它将使用 Spring 类 org.springframework.beans.propertyeditors.StringArrayPropertyEditor 将 String 转换为 String[] 而不使用分隔符(null 参数)。 如果同一项目中有人使用新的默认转换方式,就可以了。

【讨论】:

易于实施,完美解决我的代码 - 谢谢! 也适用于 Spring 4。但是,如果将 params 声明为 List 而不是 String[],则即使使用 List 作为类,它也不起作用。我没有时间深入研究 Spring 内部以找出原因,只是更改为使用 String[] 作为我的参数,而不是 List JavaDoc 中并不清楚,但似乎不支持使用 null 作为分隔符参数。最好使用逗号以外的实际字符。 这个解决方法也适用于我在 Spring Boot 版本 1.5.19 中使用 @RestController 对我来说最好的答案。 @beaudet,它是 StringArrayPropertyEditor,所以它自然不适用于列表。试试binder.registerCustomEditor(List.class, new CustomCollectionEditor(List.class));【参考方案6】:

以下是我在 MVC 请求中绕过逗号分隔的过程

这是我对这些类型解决方案的 Springs 演变的理解: 文档对于最新的解决方案非常模糊。

首先是实现接口 WebMvcConfig 的 WebMvcConfigAdapter。这最终被弃用了

这已被 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;
            
        ;
    

【讨论】:

我的意思是代码中表示的 WebMvcConfigurer【参考方案7】:

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

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

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

【讨论】:

以上是关于Spring 3.0.5中如何防止参数绑定解释逗号?的主要内容,如果未能解决你的问题,请参考以下文章

如何防止特定组访问某些 Spring 应用程序上下文文件?

防止整数值在 ASP.NET Web API 模型绑定中设置布尔参数?

yii database操作绑定参数防止sql注入

Spring JdbcTemplate 查询绑定list参数

带有绑定参数的预处理语句优于带有转义/引用参数的插值语句的原因

如何指定一个请求参数绑定到某个控制器方法参数而不是Spring MVC中的模型属性?