2

I have a Spring 3.2 Controller with basic request mappings like

@RequestMapping("/action")
public String action(@RequestParam("param") String param) {
    //do stuff...
    return "view";
}

This controller handles links created by non-technical business users. Sometimes the users mess it up and create links with duplicate parameters, e.g.,

www.example.com/action?param=value&param=value

The parameter is an exact duplicate and probably a copy/paste error.

My problem is that Spring is concatenating these dupes together, so that the url above will give "value,value" for param, when I want only "value".

What is a good way to detect and handle these duplicates? I know I could change all my @RequestParams to List<String>s and go from there, but that's a whole lot of boilerplate over dozens of request mappings.

Ideally there would be a way to intercept and modify the url parameters before Spring attempts to bind them -- but only for this controller.

5
  • Strictly speaking, ?param=value&param=value is not the same as ?param=value only. So if Spring did that "merging" in any way, it would be an invalid behavior. So, yeah, you have to implement it yourself. Be it through a new ...ArgumentResolver or ...Interceptor.
    – acdcjunior
    Commented Apr 2, 2014 at 17:20
  • A HandlerInterceptor was my first thought too, but you can't modify parameters from there. Commented Apr 2, 2014 at 17:23
  • Yeah, it is not possible to change the attributes. To achieve what you want, you have to, in the interceptor, wrap the HttpServletRequest in a HttpServletRequestWrapper (see: stackoverflow.com/questions/1413129/… ).
    – acdcjunior
    Commented Apr 2, 2014 at 18:06
  • If you mean a servlet Filter rather than a Spring HandlerInterceptor then yes. I feel like a solution should exist higher than the servlet level though. Commented Apr 2, 2014 at 19:21
  • In my case for spring-web 5.3.25 it's duplicated for no reason...
    – Martin P.
    Commented Apr 13, 2023 at 15:07

3 Answers 3

4

I found that I can register a custom String property editor to do this.

class DuplicateParameterReducingPropertyEditor extends PropertyEditorSupport {

    Object value;

    @Override
    public void setValue(Object value) {
        if (value instanceof String[]) {
            String[] strings = (String[])value;
            Set<String> unique = Sets.newHashSet(strings);
            this.value = unique.toArray();
        } else {
            this.value = value;
        }
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        this.value = text;
    }

    @Override
    public String getAsText() {
        return value.toString();
    }

    @Override
    public Object getValue() {
        return value;
    }

};

I added this to my controller:

@InitBinder
public void initBinder(WebDataBinder binder) {

    PropertyEditor stringEditor = new DuplicateParameterReducingPropertyEditor();
    binder.registerCustomEditor(String.class, stringEditor);
}

So whenever Spring encounters a @RequestParam-annotated String method argument, the PropertyEditor is invoked to transform the incoming data if needed. In the case of duplicate parameters, Spring passes a String[] of the values to the property editor setValue, which I can then manipulate.

This does have the results I am looking for. I'm not sure of all the implications of this, though, so I can't endorse it as good solution yet. Not having to alter any handler method signatures is a big plus though.

1

A good idea would be to extend AbstractNamedValueMethodArgumentResolver with your own strategy. Then the strategy could be used wherever you deem necessary.

This strategy only works for Spring 3.1+ which is not a problem for you since you are using Spring 3.2

3
  • 1
    Exactly what I was thinking :)
    – Bart
    Commented Apr 2, 2014 at 16:50
  • Could you provide an example or link to documentation? I know how to register a custom HandlerMethodArgumentResolver but I'm not sure how to override the default @RequestParam resolver. Commented Apr 2, 2014 at 17:21
  • I don't think you can override the default behavior... What I had in mind was to write a new resolver with it's own annotation. However I would be very happy to see some example of overriding the default behavior!
    – geoand
    Commented Apr 2, 2014 at 17:33
1

I faced the same issue in Spring boot. Eventually I came up with this solution using converter, in case it helps anyone.

This method should be added as part of your WebMvcConfigurer class.

@Override
public void addFormatters(FormatterRegistry registry) {
    // Duplicate query parameters converter
    registry.addConverter(new Converter<String[], String>() {
        public String convert(String[] arr) {
            return arr[arr.length - 1];             // Return the last value
        }
    });
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.