Home > Software design >  Asp.net Core 3.1 Binding: Action with a Dictionary as parameter in GET
Asp.net Core 3.1 Binding: Action with a Dictionary as parameter in GET

Time:10-27

We are defining an action that will be used to search documents stored in a repository; the interface allows to specify a list of filters to narrow down the result.

[Route("search")]
        [HttpGet]
        [ProducesResponseType(typeof(List<DocumentDto>), (int)HttpStatusCode.OK)]
        public async Task<ActionResult<DocumentDto>> GetDocuments(
        [FromServices] IDocumentManager documentManager,
        [FromQuery] Dictionary<string, string> filters)
        {
            //Do something
        }

We are using a parameter like Dictionary<string, string> filters in the action signature because we would like to implement a search using a GET method but with a dynamic list of parameters.
Filters are just a list of key:value objects which the action will pass to a Database, the only layer that knows how to handle them.
We consume the service calling an url like this:

  /search?filter1=value1&filter2=value2&filter3=value3

The binding seems to "work" and filters is populated with something like this:

filters
[0] {[filter1, value1]}
[1] {[filter2, value2]}
[2] {[filter3, value3]}

Checking the binding chapter related to Dictionaries, it seems that the parameters passed in query string like we are doing is not documented.

Are we seeing a binding side effect?
Is the syntax we are using to pass filters from query string to a dictionary supported?

CodePudding user response:

It's a bit more or less problematic using binding like that. The dictionary on your controller is catching all the query parameters, such that if your query string is:

/search?filter1=value1&filter2=value2&filter3=value3&random=randomValue

You will see in the dictionary:

filters
[0] {[filter1, value1]}
[1] {[filter2, value2]}
[2] {[filter3, value3]}
[3] {[random, randomValue]}

When ASP.NET Core creates the context for model binding for a parameter, the model name comes from one of the sources:

  1. Explicit name (such as [FromQuery(Name = ...)]
  2. From the value provider (in this case QueryStringValueProvider)

In your example, none of the above is true, so the model name in the binding context will be empty. This will cause the DictionaryModelBinder to populate arbitrary parameters in the query string to the dictionary.

I'd recommend fixing your query string as per the documentation you referenced.

  • Related