How to customize split-pattern for request parameter
My problem is that I have the following URL:
http://localhost:8080/shiSolrClient/app/shi/search?q=xyz&fq=author:"Max, Muster"
I have a bean that maps these requestParameters:
public class SearchParams {
private String q = "";
private String[] fq;
// getters goes here...
}
My Problem is that Spring automatically split the fq-parameter on the comma. So in my bean there are two Strings in fq:
String[0]: author:"Max
String[1]: Muster"
I don't want this behaviour. What I want is to tell Spring to split on '&'-tokens not on ','-tokens. E.g.
http://localhost:8080/shiSolrClient/app/shi/search?q=xyz&fq=author:"Max, Muster"&content:"someContent"
fq=
String[0]: author:"Max, Muster"
String[1]: content:"someContent"
Can anyone tell me how to archive this in Spring MVC 3
My Controller is as follows:
@RequestMapping(value = "search", method = RequestMethod.GET)
public String search(SearchParams searchParams, BindingResult bindResult, Model
model) {
SolrQuery solrQ = getBasicQuery(searchParams).setQuery(searchParams.getQ());
for(String fq : searchParams.getFq()) {
solrQ.setParam("fq", fq);
}
try {
QueryResponse rsp = getSolrServer().query(solrQ);
model.addAttribute("solrResults", transformResults(rsp.getResults(),
rsp.getHighlighting(), searchParams, rsp));
model.addAttribute("facetFields", transformFacets(rsp.getFacetFields(),
rsp.getFacetDates(), searchParams));
model.addAttribute("pagination", calcPagination(searchParams,
rsp.getResults()));
...
}
And my Spring-Config looks like this:
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** -->
<resources mapping="/resources/**" location="/resources/" /&g开发者_StackOverflow社区t;
<!-- Configure Apache Tiles for the view -->
<beans:bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<beans:property name="definitions">
<beans:list>
<beans:value>/WEB-INF/views/layout/layouts.xml</beans:value>
<beans:value>/WEB-INF/views/hitlist/views.xml</beans:value>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<beans:property name="requestContextAttribute" value="requestContext"/>
<beans:property name="viewClass"
value="org.springframework.web.servlet.view.tiles2.TilesView"/>
</beans:bean>
<beans:bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basenames">
<beans:list>
<beans:value>global</beans:value>
<beans:value>hitlist</beans:value>
<beans:value>local/messages</beans:value>
</beans:list>
</beans:property>
</beans:bean>
<!-- Scans within the base package of the application for @Components to configure
as beans -->
<context:component-scan base-package="com.shi.solrclient.web" />
I ran into a similar problem trying to use a @RequestParameter with List (it kept getting chopped up at commas.
The solution I came up with was to use a WebRequest parameter in my controller method, which allows me to use request.getParameterValues("blar")
@RequestMapping(value = "search", method = RequestMethod.GET)
public String search(WebRequest req, BindingResult bindResult, Model model) {
String[] searchParams = req.getParameterValues("fq")
Registering a custom editor for String[] as proposed by Eugene does not work because String[] is converted to String by a CollectionToStringConverter before any custom editor usage.
In order to use another separator than comma (which is CollectionToStringConverter default behavior) you need to add your own custom converter as explained in Spring MVC reference documentation.
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="example.MyCustomCollectionToStringConverter"/>
</set>
</property>
</bean>
For implementing MyCustomCollectionToStringConverter, you can't extend CollectionToStringConverter since it is final but you can have a look to its source code to create your own.
final class MyCustomCollectionToStringConverter implements ConditionalGenericConverter {
private static final String DELIMITER = "&";
private final ConversionService conversionService;
public MyCustomCollectionToStringConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Collection.class, String.class));
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType, this.conversionService);
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Collection<?> sourceCollection = (Collection<?>) source;
if (sourceCollection.size() == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
int i = 0;
for (Object sourceElement : sourceCollection) {
if (i > 0) {
sb.append(DELIMITER);
}
Object targetElement = this.conversionService.convert(sourceElement, sourceType.elementTypeDescriptor(sourceElement), targetType);
sb.append(targetElement);
i++;
}
return sb.toString();
}
}
Since noone has come up with a "proper" answer to this, your easiest option is probably to not get Spring to split the parameter at all, but instead to add the split logic in the getter method:
public class SearchParams {
private String q = "";
private String fq;
public String[] getSplitFq() {
// split and return the array
}
}
This is probably easier than trying to persuade Spring to do it to your own specification.
You can use @InitBinder annotation to configure array binding. Something like this should work:
@InitBinder("fq")
public void fqBinderInit(WebDataBinder binder) {
binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor("&"));
}
You can read more detailed explanation in Spring MVC documentation
精彩评论