1

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)
{ 
    // ...
}
3
  • The way you sent the values for ProductAttributes in Postman is incorrect. It should be: ProductAttributes.0.attributeId: 5, ProductAttributes.0.value: string. It is flatten JSON notation. Commented Oct 16, 2023 at 1:52
  • Possible duplicates: .NET Core API - Send array of object as FormData via Postman Commented Oct 16, 2023 at 2:08
  • Swagger has issue with enumerables in forms. I'd suggest using [FromBody] jsons, or you'd need some workarounds. Commented Oct 16, 2023 at 2:57

1 Answer 1

3

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

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.