Consider the following controller method:
@RequestMapping(value = \"/test\", method = RequestMethod.GET)
public void test(@RequestParam(value = \"fq\", requi
javanna already pointed out the correct root cause. I just wanted to further point out that you can also remove the StringToArrayConverter altogether as shown here and here.
Its a hack but, have you considered passing your params delimited with '-'
/test?fq=foo-bar results in fq = foo-bar
/test?fq=foo-bar&fq=bash results in fq = foo-bar|bash
Or any other delimiter maybe ~, or !,or ^, or ???
As suggested by Philip Potter, I'm posting the "update" to my question as an answer, as it might've been easy to miss...
Down-grading from Spring 3.0.5.RELEASE to 3.0.4.RELEASE fixed the issue, when using the @RequestParam
annotation, suggesting it is a bug with 3.0.5.
However, it does NOT fix the related issue, when binding to a form-backing bean - which is what I have in my webapp. I've tested all version back to 3.0.0.RELEASE and get the same result (/test?fq=foo,bar
produces 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(), "|"));
}
where SearchRequestParams
contains a field String[] fq
.
If anyone has a fix for this, I'll gladly accept their answer.
I've tested your code: it's unbelievable, but I can't reproduce your issue. I've downloaded the latest version of spring (3.0.5), this is my controller:
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(), "|"));
}
}
this is my SearchRequestParams class:
package test;
public class SearchRequestParams {
private String[] fq;
public String[] getFq() {
return fq;
}
public void setFq(String[] fq) {
this.fq = fq;
}
}
and this is my simple spring configuration:
<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>
I've tested my code within tomcat 7.0.8; when I type http://localhost:8080/testweb/test/params.htm?fq=foo,bar
I'm able to read in my log file this line: DEBUG fq = foo,bar
.
What are the the differences from my code to yours? Am I doing something wrong?
I'd like to help you, so if you have any doubts or if I can do some other tests for you, it will be a pleasure.
UPDATE / SOLUTION
With your code I've reproduced the issue; you have the tag <mvc:annotation-driven />
in your dispatcher servlet configuration, so you silently use a default conversion service, instance of FormattingConversionService
, which contains a default converter from String
to String[]
that uses comma as separator.
You have to use a different conversion service bean containing your own converter from String
to String[]
. You should use a different separator, I've choosed to use ";" because it's the separator commonly used into query 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, ";");
}
}
Then you have to specify this conversion service bean in your configuration:
<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>
The issue has fixed, now you should check for any side effects. I hope you don't need in your application the original conversion from String
to String[]
(with comma as separator). ;-)
I have found the most elegant and the shortest way for me - add @InitBinder
to a @Controller
:
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor(null));
}
It will convert String to String[] without using separator (null
param), with Spring class org.springframework.beans.propertyeditors.StringArrayPropertyEditor.
If someone in same project will use new default conversion way, it will be ok.
The following is my journey of bypassing comma delimiting in a MVC request
This is my understanding of Springs evolution of these type of solutions: The documentation is very vague about what is the latest solution.
First was WebMvcConfigAdapter implementing the interface WebMvcConfig. This was eventually deprecated
That was replaced by WebMvcConfigSupport. This was eventually deprecated but it was better than the first solution. The main issued was that it turned off the MVC auto-configuration and had side issues like swagger.html not working and actuator info being missing pretty format and the date became a large decimal
The latest is a revised interface WebMvcConfig that implements default methods using Java 8 features.
Create a class in my case, WebConfigUUID could be AnyClass, that implements the later version of WebMvcConfig
This allows you to change what you need, in our case a custom converter, without impacting anything else or having to override another to get swagger to work, or deal with actuator info output
The following are the two classes that implemented my change to bypass comma delimiting of strings to a list
in processing the MVC request:
It still produces a list of strings but with only one value.
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;
}
};
}
}