'Call Webapi with Dictionary<String, object> as parameter from ASP .NET MVC Application

I have a WebApi defined as below

    public ActionResult DoSomeAction([FromForm(Name = "file")] IFormFile dataFile, 
                               Dictionary<string,object> collection)
    {
    //do something
    }

I am trying to call this from my client as shown below,

            using (var client = new HttpClient())
            {
                var api_Uri = Environment.GetEnvironmentVariable("API_URL");                
                client.BaseAddress = new Uri(api_Uri);
                client.DefaultRequestHeaders.Clear();
                //Define request data format
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                Dictionary<string, object> data = new Dictionary<string, object>();
                data.Add("a", "sasas");
                data.Add("b", "sasas");
                data.Add("", "sasas");

              
                var inputSerialized = JsonSerializer.Serialize(data);
                var stringContent = new StringContent(inputSerialized , Encoding.UTF8, "application/json");                 


                var requestString = string.Format("api/DoSomeAction?selectedRule={0}", stringContent);

                HttpResponseMessage Res = await client.PostAsync(requestString, multipartContent);                 

                 
            }

multipartContent is MultipartFormDataContent which contains File information. The above code is not working. Some guidance would be appreciated.



Solution 1:[1]

I was able to solve this my implementing a custom IModelBinder. I moved IFormFile and Dictionary to a class. While creating the request as below ,

    internal MultipartFormDataContent GetRequestParams(IFormFile FilePath)
    {
        MultipartFormDataContent multipartContent = GetFileContent(FilePath);
        var dataExtractor = new DataExtractor();
        var dictionaryData = dataExtractor.GetDictionary(); //This return  Dictionary<string,object>
        var serialisedData = JsonSerializer.Serialize(dictionaryData);
        var stringContent = new StringContent(serialisedData, Encoding.UTF8, "application/json");

        multipartContent.Add(stringContent, "MyCollection");
        return multipartContent;
    }


    private MultipartFormDataContent GetFileContent(IFormFile FilePath)
    {
        byte[] data;
        using (var br = new BinaryReader(FilePath.OpenReadStream()))
        {
            data = br.ReadBytes((int) FilePath.OpenReadStream().Length);
        }
        ByteArrayContent bytes = new ByteArrayContent(data);
        MultipartFormDataContent multiContent = new MultipartFormDataContent();
        multiContent.Add(bytes, "File", FilePath.FileName); 
        //Key is "File", bcos my property name in class is File. This should match
        return multiContent;
     }
    

Custom class containing the data

  public class Input 
  {
    public IFormFile File { get; set; }

    [ModelBinder(BinderType = typeof(FormDataJsonBinder))]
    public Dictionary<string,object> MyCollection{ get; set; }
    
 }

Custom IModelBinder implementation

 public class FormDataJsonBinder : IModelBinder
{     
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        var modelName = bindingContext.ModelName;

        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);

        if (valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);

        var value = valueProviderResult.FirstValue;

        if (string.IsNullOrEmpty(value))
        {
            return Task.CompletedTask;
        }

        try
        {
            var result = JsonSerializer.Deserialize(value, bindingContext.ModelType);
            bindingContext.Result = ModelBindingResult.Success(result);
        }
        catch (Exception ex)
        {                
            bindingContext.Result = ModelBindingResult.Failed();
        }

        return Task.CompletedTask;
    }
}

}

Web Api Signature

 public IActionResult ExecuteRule([FromForm] Input inputdata)
 { 
 // Do something
 }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 harin04