3

We have many @RestController receiving phrases in common language written by users. Phrases can be very long and contains punctuation, like periods and, of course, commas.

Simplified controller example:

@RequestMapping(value = "/countphrases", method = RequestMethod.PUT)
public String countPhrases(
    @RequestParam(value = "phrase", required = false) String[] phrase) {

  return "" + phrase.length;
}

Spring boot default behaviour is to split parameters values at comma, so the previous controller called with this url:

[...]/countphrases?phrase=john%20and%20me,%20you%and%her

Will return "2" istead of "1" like we want. In fact with the comma split the previous call is equivalent to:

[...]/countphrases?phrase=john%20and%20me&phrase=you%and%her

We work with natural language and we need to analyze phrases exactly how the users wrote them and to know exactly how many they wrote.

We tried this solution: https://stackoverflow.com/a/42134833/1085716 after adapting it to our spring boot version (2.0.5):

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // we hoped this code could remove the "split strings at comma"
        registry.removeConvertible(String.class, Collection.class);
    }
}

But it doesn't work.

Anyone know how to globally remove the "spring boot split string parameters at comma" behaviour in spring boot 2.0.5?

Znheb
  • 121
  • 8
  • 1
    Why don't you go for POST and retrieve values in body? – Pratik Ambani Sep 11 '19 at 10:38
  • POST, PUT, PATCH are used when posting data into a service, and here you usually use the body because then you can take advantage of SSL and the data sent is encrypted. GET usually returns a body, but the data returned is filtered by what query params you are sending. For example you do a search in a search engine. You want to GET the results filtered on what you searched for "bananas". The query string contains bananas, it will return in the body my results. – Toerktumlare Sep 11 '19 at 11:04
  • Some solutions are here https://stackoverflow.com/questions/4998748/how-to-prevent-parameter-binding-from-interpreting-commas-in-spring-3-0-5 – xtian Feb 24 '22 at 21:29

2 Answers2

5

I find the solution. To override a default conversion we must add a new one. If we remove the old one only it doesn't work.

The correct (example) code should be:

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.removeConvertible(String.class, String[].class);
        registry.addConverter(String.class, String[].class, noCommaSplitStringToArrayConverter());
    }

    @Bean
    public Converter<String, String[]> noCommaSplitStringToArrayConverter() {
        return new Converter<String, String[]>() {
            @Override
            public String[] convert(String source) {
                String[] arrayWithOneElement = {source};
                return arrayWithOneElement;
            }
        };
    }
}

This way any controller like the one in the main question will not split parameters values:

  • [...]/countphrases?phrase=a,b will return 1 (and fq=["a,b"])
  • [...]/countphrases?phrase=a,b&phrase=c,d will return 2 (and fq=["a,b", "c,d"])
Znheb
  • 121
  • 8
1

Replacing your formatter registry with a completely new list could make you loose some needed default formatters that would come with the framework. This will also disable all String-To-Collections parsing for the entire application, on every endpoint, such that if you want to a request filter such as the following at another endpoint, it won't work:

identifiers = 12,34,45,56,67

Solution: Just change your delimiter into something else... # or ; or $

identifiers = 12;23;34;45;56

This is what I have been doing, so I don't mess with all the goodies in the formatter registry.

eadjei
  • 2,066
  • 2
  • 14
  • 7
  • In our use case your solution was not possibile because the parameter values were human written text and there was no pre-processing available, something like "replace all commas with pipe before calling the api". We didn't have this level of control so we had to change the spring boot behaviour. – Znheb Oct 26 '21 at 08:23