6

I have a little problem with passing Dictionary as part of the parent object as a query parameter.

So, I have a method that gets a FormulationParameters variable.

public async ValueTask<IActionResult> Get([FromQuery] FormulationParameters formulationParameters)
{
     return Ok(await _service.Get(formulationParameters));
}

The FormulationParameters class looks like this:

public class FormulationParameters
{
   //other simple properties, e.g. string, int
        
   public Dictionary<string, string> Substitute { get; set; }
}

When I was using Postman to pass these parameters I filled its form "Query Params" like this and everything was okay:

  • I passed a Key as Substitute[FirstValue] and its Value as 10

And in the Substitute dictionary I got a record where the Key is equal to "FirstValue" and its Value is 10.

But when I tried to write some tests for my API via Refit I found out that this didn't work as I expected.

So, I have a method in the Refit interface:

[Get("/api/path")]
Task<HttpResponseMessage> GetFormulations([Query] FormulationParameters parameters]

And I fill all properties of FormulationParameters in code. When I Debug my tests I see that in the API method (which is defined above) all properties of the formulationParameters variable are filled except this Substitute dictionary.

I was looking for this problem on the Internet and found some solutions. I tried them all but it didn't work for me.

First of all, I tried to use [JsonDataExtension] attribute for this property. As I read it means that if you put this attribute for some property all parameters from Request with no matching class member will be written into the specified collection. So, I changed Dictionary<string, string> to Dictionary<string, object>. Also, I changed how data is sent via the Refit method: I moved Substitute from the main class and sent it as a separate parameter, so the query string looked like this:

?Codes=Code1,Code2&Type=Type&Language=en&FirstValue=1&SecondValue=10

But, no effect, Substitute still was not filled.

Also, I tried to write a custom converter for this property, but no effect, it even didn't invoke when the request came.

The one thing that worked was an action filter. I had found this solution in the StackOverflow post where the same problem was discussed. A method from this filter looks like this:

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var notMatchedProperties = new Dictionary<string, string>();
    
            // because in ActionArguments there is only one variable - formulationParameters
            var mainVariableKey = context.ActionArguments.First().Key;
            
            // the content of FormulationParameters class
            var innerParts = context.ActionArguments.First().Value as FormulationParameters;
            
            // conversion from class to dictionary
            // the names of the properties are the keys, and their values are values in the dictionary
            var classAsDictionary = ClassToDictionaryConverter.Convert(innerParts);
            var keys = classAsDictionary.Keys;
    
            
            foreach (var kvp in context.HttpContext.Request.Query)
            {
                if (!keys.Contains(kvp.Key, StringComparer.InvariantCultureIgnoreCase))
                {
                    notMatchedProperties.Add(kvp.Key, kvp.Value);
                }
            }
    
            // fill this dictionary with no matched properties from input
            innerParts!.Substitute = notMatchedProperties;
            
            // rewrite the existing value with the new value
            context.ActionArguments[mainVariableKey] = innerParts;
            base.OnActionExecuting(context);
        }

It is not good either, because it does not work properly when I try to pass some parameters via Postman. For example: Codes[0]=1&Codes[1]=2, but it works fine when I write like this Codes=1&Codes=2 - it's mapped into Codes : {1, 2} and it is considered like one key Codes. Also, it is not universal, and it looks like a sort of a workaround.

I don't know if it's okay to use this action filter, or there is another solution that I haven't tried yet (but I don't know about it). And if it's okay if I will use this filter, and my users and I always need to remember how to pass these parameters properly. So, I'm looking for a more universal way to parse these parameters when requests come from different sources (like Refit, Swagger, Postman).

P.S. I am using .NET 5, and my application is Web API type

Jane
  • 61
  • 3

0 Answers0