Search code examples
c#asp.net-coreasp.net-web-apimultipartform-dataclean-architecture

FormData parameter is always null in endpoint when passing a collection of IFormFile


The following CreateImages endpoint is in my C# .NET Web API. Testing with Postman and Swagger UI, when I upload files and send the request, files parameter below is always null. I don't have this issue if I update the parameter to a single IFormFile, it is only when I try to pass a list.

public class Images : EndpointGroupBase
{
    public override void Map(WebApplication app)
    {
        app.MapGroup(this)
            .DisableAntiforgery()
            .RequireAuthorization()
            .MapPost(CreateImages);
    }

    public async Task<IResult> CreateImages(ISender sender, [FromForm] IList<IFormFile> files)
    {
        return Results.Ok(await sender.Send(new CreateImagesCommand(files)));
    }
}

I get the same issue with the following types: IList, List, ICollection, IEnumerable. If I pass an array (IFormFile[]), even with the [FromForm] tag, It will not be registered as multipart/form-data but expects a list of strings. I assume this is not recommended anyways. Also, as mentioned, if I update this endpoint to instead accept a single IFormFile, everything works as expected - value is not null and debugger can proceed with item as expected IFormFile type.

enter image description here


Solution

  • After playing around with this, it seems Minimal API expects only IFormFileCollection as a valid type for multiple file uploads. Changing the parameter will resolve the issue.

    For anyone else who wants to try, spin up an empty project in .NET 8.0 and add the endpoint using Minimal API below in Program.cs. Try different types for the files parameter; I tried ICollection, IList, List, IReadOnlyCollection. All of these fail with 415 Unsupported Type.

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    // POST endpoint for multiple file upload
    app.MapPost("/upload-multiple", async (IFormFileCollection files) =>
    {
        if (files.Any())
        {
            foreach (var file in files)
            {
                var filePath = Path.Combine("uploads", file.FileName);
    
                using (var stream = new FileStream(filePath, FileMode.Create))
                {
                    await file.CopyToAsync(stream);
                }
            }
    
            return Results.Ok("Files uploaded successfully.");
        }
    
        return Results.BadRequest("No files uploaded.");
    }).DisableAntiforgery();
    
    app.Run();
    

    Not sure if this is specific to later .NET versions, or if this is mentioned in the docs anywhere that I couldn't find.