Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why escaped comma is the delimiter for @RequestParam List<String>? #23820

Closed
akrikheli opened this issue Oct 16, 2019 · 3 comments
Closed

Why escaped comma is the delimiter for @RequestParam List<String>? #23820

akrikheli opened this issue Oct 16, 2019 · 3 comments
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: declined A suggestion or change that we don't feel we should currently apply

Comments

@akrikheli
Copy link

Affects: < Spring Boot 2.1.2.RELEASE>


I have a simple rest controller with the method the get requests mapped on:

@GetMapping("/cis_list")
public Map<String, CisFrontendDto> cisList(@RequestParam(value = "cis") List<String> cis) {
        log.debug("#cisList: started with cisIds = {}", cis);
        return cisService.findByIds(cis);
    }

I try to pass string hello, world! as a single element of List cis parameter:

curl -X GET "http://127.0.0.1:8095/api/v3/facade/cis/cis_list?cis=hello%2C%20world!" -H "accept: /"

I have expected the list contains only one string but аs it turned the list contains two elements although the comma has been escaped in source request.

image

In this way there's not a difference between requests:

curl -X GET "http://127.0.0.1:8095/api/v3/facade/cis/cis_list?cis=hello%2C%20world!" -H "accept: /"

and

curl -X GET "http://127.0.0.1:8095/api/v3/facade/cis/cis_list?cis=hello,%20world!" -H "accept: /"

So, I'm not able to map parameters of HTTP GET requests to Java List if they contain escaped commas.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Oct 16, 2019
@rstoyanchev rstoyanchev changed the title [RestController] Why escaped comma is the delimiter for string list? Why escaped comma is the delimiter for @RequestParam List<String>? Oct 17, 2019
@rstoyanchev
Copy link
Contributor

rstoyanchev commented Oct 17, 2019

This is due to the StringToCollectionConverter which gets involved if there is 1 request parameter value and we have to convert from String to List<String>. When there are multiple values the ArrayToCollectionConverter is used, so the behavior is specific to single values. It is a bit surprising I will admit, but we're unlikely to change this behavior at this time. Rather I can suggest that you remove the String to Collection conversion through the formatters callback in the WebMvcConfig.

@nschrader
Copy link

nschrader commented Sep 6, 2021

Let's be honest, it's just broken. The parameter is sent with a percent escaped , (%2C) and according to RFC 3986, parameters need to be separated by an unescaped ,. Things that belong together get ripped apart, although we have the information that they shouldn't.

(And the RFC even explicitly states that commas are allowed as parameter values as long as they are percent escaped.)

@helderhernandez
Copy link

I managed to solve the problem by applying the following:

The trick is done by the following lines of code

@InitBinder
public void initBinder(WebDataBinder binder) {
  binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor(null));
}

My classes were as follows:

Clase Search (works as a wrapper)

public class Search {
	private String[] search = new String[] {};

	public Search() {
		super();
	}

	public Search(String[] search) {
		super();
		this.search = search;
	}

	public String[] getSearch() {
		return search;
	}

	public void setSearch(String[] search) {
		this.search = search;
	}

	public List<String> toList() {
		return Arrays.asList(this.search);
	}

	@Override
	public String toString() {
		return "Search [search=" + Arrays.toString(search) + "]";
	}
}

Endpoint

@InitBinder
public void initBinder(WebDataBinder binder) {
  binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor(null));
}

@GetMapping("/search")
@ResponseStatus(HttpStatus.OK)
@ApiOperation(value = "Search providers")
//@formatter:off
@ApiImplicitParams({
	@ApiImplicitParam(
	    name = "search", 
	    allowMultiple = true, 
	    dataType = "string", 
	    paramType = "query", 
	    value = "Search by field: description"
	) 
})
//@formatter:on
public List<String> search(Search search) {
	System.out.println(search.toList().size());
	search.toList().forEach(System.out::println);
	
	//..... your code
}

Results

image

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

No branches or pull requests

6 participants