micronaut-projects / micronaut-core

Micronaut Application Framework
http://micronaut.io
Apache License 2.0
6.04k stars 1.06k forks source link

Binding comma separated query value to List #2157

Closed sdelamo closed 4 years ago

sdelamo commented 4 years ago

Section 3.2.1 of RFC 6570

For a variable that is a list of values, expansion depends on both the expression type and the presence of an explode modifier. If there is no explode modifier, the expansion consists of a comma- separated concatenation of the defined member string values. If there is an explode modifier and the expression type expands named parameters (";", "?", or "&"), then the list is expanded as if it were an associative array in which each member value is paired with the list's varname. Otherwise, the value will be expanded as if it were a list of separate variable values, each value separated by the expression type's associated separator as defined by the table above.

 Example Template     Expansion

   {count}            one,two,three
   {count*}           one,two,three
   {/count}           /one,two,three
   {/count*}          /one/two/three
   {;count}           ;count=one,two,three
   {;count*}          ;count=one;count=two;count=three
   {?count}           ?count=one,two,three
   {?count*}          ?count=one&count=two&count=three
   {&count*}          &count=one&count=two&count=three

Given:

package example;

import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.annotation.QueryValue;

import javax.annotation.Nullable;
import java.util.List;

@Controller
public class HomeController {

    @Produces(MediaType.TEXT_PLAIN)
    @Get("{?fields}")
    int index(@Nullable @QueryValue List<String> fields) {
        return fields == null ? 0 : fields.size();
    }
}

I was expecting:

curl http://localhost:8080?fields=1,2,3 curl http://localhost:8080?fields=1&fields=2&fields=3"

to return 3.

Only the latter returns 3.

Using an explode ("*") modifier yields the same results.

    @Produces(MediaType.TEXT_PLAIN)
    @Get("{?fields*}")
    int index(@Nullable @QueryValue List<String> fields) {
        return fields == null ? 0 : fields.size();
    }

The workaround we are using is exploding the items manually if the there is only one item.

return fields == null ? 0 : (fields.size() == 1 ? Arrays.asList(fields.get(0).split(",")).size() : fields.size());

Environment Information

jameskleeh commented 4 years ago

You're describing the expansion of a template. I don't think the template is being expanded given an argument. We're receiving query values and attempting to bind them to the argument. So I don't think the RFC you've included there applies to this use case.

jameskleeh commented 4 years ago

I don't think we can or perhaps should assume that a string value with a comma in it represents multiple values when binding to a list. Should ?fields=Hello, Sam be one item in the list or two?

jameskleeh commented 4 years ago

Also duplicates https://github.com/micronaut-projects/micronaut-core/issues/1610

archenroot commented 2 years ago

@jameskleeh - the parsing of comman separated values in list is valid request and actually default way of working with url params. Your statement of: Should ?fields=Hello, Sam be one item in the list or two?

Well it depends if the field is defined as String or List, so respectively it will be either 1 or 2 values.