Search code examples
c#restasp.net-core-mvchttp-headersmultipartform-data

c# [FromForm] List<IFormFile> always results in null


I want to test an REST API endpoint written in C# (.NET 8.0).

The endpoint /import should take multiple files from the TestFiles folder:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.MapPost("/import", Import).DisableAntiforgery();

static async Task<IResult> Import([FromForm] List<IFormFile> xyz)
{
    if (xyz == null || xyz.Count == 0)
    {
        return Results.BadRequest("No files were provided.");
    }

    // This is never reached !!!

    return Results.Ok();
}

app.Run();

The test looks like this:

using Microsoft.AspNetCore.Mvc.Testing;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;

namespace Importer.Tests
{
    public class IntegrationTests(WebApplicationFactory<Program> factory) : IClassFixture<WebApplicationFactory<Program>>
    {
        private readonly WebApplicationFactory<Program> _factory = factory;
        private readonly string _testFilesPath = Path.Combine(Environment.CurrentDirectory, "TestFiles");

        private MultipartFormDataContent CreateMultipartFormDataFromTestFiles(List<string> fileNames)
        {
            var formContent = new MultipartFormDataContent();

            foreach (var fileName in fileNames)
            {
                string filepath = Path.Combine(_testFilesPath, fileName);
                var fileBytes = File.ReadAllBytes(filepath);
                var fileContent = new ByteArrayContent(fileBytes);
                fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
                formContent.Add(fileContent, "xyz", fileName);
                // "files" parameter name should be the same as the server side input parameter name
                //https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-8.0#match-name-attribute-value-to-parameter-name-of-post-method
            }

            return formContent;
        }

        [Fact]
        public async Task API_Import_File_Should_Return_Http_Status_OK()
        {
            List<string> fileList = new List<string> { "Input.txt" };
            var form = CreateMultipartFormDataFromTestFiles(fileList);

            using var client = _factory.CreateClient();
            var response = await client.PostAsync("/import", form);

            Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        }
    }
}

The problem is the list "xyz" is always null!

If the [FromForm] tag is removed, the response is:

StatusCode: 415, ReasonPhrase: 'Unsupported Media Type'

If the endpoint /import is changed to handle only one file => IFormFile, everything works!

Any suggestions are more than appreciated. Thanks


Solution

  • In Minimal APIs, file uploads support using IFormCollection, IFormFile, and IFormFileCollection in .NET 8. Notice, List<IFormFile> does not support so that you need change your code to:

    static async Task<IResult> Import([FromForm] IFormFileCollection xyz)
    {
        if (xyz == null || xyz.Count == 0)
        {
            return Results.BadRequest("No files were provided.");
        }
        foreach (var file in xyz)
        {
            // ...
        }
    
        return Results.Ok();
    }