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. 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/… ). 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... 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.

Sign up to request clarification or add additional context in comments.

Comments

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 Comments

Exactly what I was thinking :)
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.
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!
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
        }
    });
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.