asp.net-coreasp.net-core-mvccustom-model-binder

Custom ASP.Net Core JSON model binder


My posted JSON object is this:

{{
 "emails": [
        {
          "To": "info@gmail.com",
          "Subject": "Subject",
          "Body": "Body",
          "ID": "d3d13242-6eff-4c57-b718-ef5ad49fe301"
        },
        {
          "To": "hr@gmail.com",
          "Subject": "Subject",
          "Body": "Body",
          "ID": "101edaf0-fcb4-48fc-9e9e-0d7492b591b0"
        }
      ]
}}

By default ASP.NET model binder will not bind this JSON object and as you can see here I get always null when I send post request to the API:

[HttpPost, Route("Send")]
public async Task<IActionResult> Send(Email[] emails)
{
  var toSave = from email in emails
                         select new EmailQueueItem
                         {
                             Html = true,
                             To = email.To,
                             Subject = email.Subject,
                             Body = email.Body
                         };

   await Database.BulkInsert(toSave.ToArray());

   return Ok();
 }

emails property is always null. I would appreciate any help for creating custom model binder that handel this kind of JSON objects.


Solution

  • The issue is that you are actually sending an object containing one property named emails, not an array, to the controller

    Option one: The client object needs to contain just the array

     [
        {
          "To": "info@gmail.com",
          "Subject": "Subject",
          "Body": "Body",
          "ID": "d3d13242-6eff-4c57-b718-ef5ad49fe301"
        },
        {
          "To": "hr@gmail.com",
          "Subject": "Subject",
          "Body": "Body",
          "ID": "101edaf0-fcb4-48fc-9e9e-0d7492b591b0"
        }
      ]
    

    Then read the array from the request body

    public async Task<IActionResult> Send([FromBody]Email[] emails)
    

    Option 2: When you define the array like this in the client

    {
      "emails":...
    }
    

    You need to match that object setup on the controller by defining a model which contains a property called emails

    public class RequestModel
    {
        public Email[] emails { get; set; }
    }
    

    Then in the controller method, use the model and read it from the body

    public async Task<IActionResult> Send([FromBody]RequestModel emails)