dotnet core get dictionairy from form data

Viewed 84

How do I get a Dictionairy<string, string> from form-data?

View model:

public class SubmitRequestModel
{
  public Dictionairy<string, string> InputFields { get; set; }
 
  public List<IFormFile> Attachments { get; set; }
}

Action:

[HttpPut("{id:int}")]
public async Task<IActionResult> Submit(int id, [FromForm] SubmitRequestModel model)
{
  // model.InputFields is initialized but it's count is 0
  // do whatever
}

This is an API controller. Not cshtml or razor related.

So the model.InputFields is not null but it's count is 0. When I look at the raw request. I can see that the input is received, but it is not bound to the dictionairy in the model.

The values of the Request.Form collection: https://prnt.sc/11x532p

I need to use form data because we are uploading files with the request. This requires multipart/form-data.

How do I successfully parse the data to the model?


Info on how I tested this:

I have swagger set-up. I import the generated Swagger OpenAPI json in to Postman and test this way. I believe that this should be the correct request, that Swagger generated. But I'm not sure if it formatted the dictionairy the right way. It would be the right way for a JSON data request. But I'm not sure if that applicates here.

3 Answers

You can achieve this with custom IModelBinder:

public class DictionaryBinder<TKey, TValue> : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        if (bindingContext.HttpContext.Request.HasFormContentType)
        {
            var form = bindingContext.HttpContext.Request.Form;
            var data = JsonSerializer.Deserialize<Dictionary<TKey, TValue>>(form[bindingContext.FieldName]);
            bindingContext.Result = ModelBindingResult.Success(data);
        }

        return Task.CompletedTask;
    }
}

The model class will become:

public class SubmitRequestModel
{
    [FromForm]
    [ModelBinder(BinderType = typeof(DictionaryBinder<string, string>))]
    public Dictionary<string, string> InputFields { get; set; }

    public List<IFormFile> Attachments { get; set; }
}

So one solution, which is not my favourite but it works, is to parse the dictionairy manually.

First thing in the action:

if (model.InputFields == null || model.InputFields.Count == 0)
{
    var rawInputFields = Request.Form["InputFields"].ToString();
    model.InputFields = JsonSerializer.Deserialize<Dictionary<string, string>>(rawInputFields);
}
Related