Mapping list of an object in ASP.NET Core 7 Web API from multipart/form-data request

530 views Asked by At

I have an issue with model binder for bind a list of object in a multipart/form-data request to related C# class property.

This is my request in Swagger:

post request

rest of request

And in my action method, the ProductAttributes count is 0:

action method

But I expect the count to be 2.

I also test the post request with Postman:

postman

but the result is the same.

CreateProductDto class:

public class CreateProductDto
{
    public string Name { get; set; }
    public string Description { get; set; }
    public int QIS { get; set; }
    public decimal Price { get; set; }
    public float DiscountPercentage { get; set; }

    public int CategoryId { get; set; }

    public List<IFormFile> Images { get; set; }
    public List<ProductAttributeCreateDto> ProductAttributes { get; set; }
}

ProductAttributeCreateDto class:

public class ProductAttributeCreateDto
{
    public int AttributeId { get; set; }
    public string Value { get; set; }
}

CreateProduct action method:

[HttpPost("CreateProduct")]
public async Task<ActionResult> CreateProduct([FromForm]CreateProductDto createProductDto)
{ 
    // ...
}
1

There are 1 answers

0
Ruikai Feng On BEST ANSWER

You should send key-value pairs with postman as below:

enter image description here

For swagger,it's a known issue and haven't be solved till now,it would send a key value pair(ProductAttributes-json string) instead of the key-value pairs as above,if you insist on testing with swagger, a workaround for you:

Create a custom model binder

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

        var values = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        if (values.Length == 0)
            return Task.CompletedTask;
        var options = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true };

        var deserialized = JsonSerializer.Deserialize(values.FirstValue, bindingContext.ModelType, options);

        bindingContext.Result = ModelBindingResult.Success(deserialized);
        return Task.CompletedTask;
    }
}

apply the binder:

[ModelBinder(BinderType = typeof(SwaggerArrayBinder))]
    public class ProductAttributeCreateDto
    {
       
        public int AttributeId { get; set; }
        public string Value { get; set; }
    }

Result:

enter image description here