You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 8中[FromForm] List<IFormFile>始终为Null的问题求助

.NET 8 最小API接收多个IFormFile时参数始终为Null的解决方法

问题场景

我基于.NET 8编写的最小API中,/import端点需要批量接收文件,但参数List<IFormFile> xyz始终返回null。移除[FromForm]标签后会收到415 Unsupported Media Type响应;若将端点改为接收单个IFormFile则完全正常。

后端核心代码:

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.");
    }

    // 此处代码从未执行

    return Results.Ok();
}

app.Run();

测试代码:

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);
            }

            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);
        }
    }
}

解决方法

问题出在测试代码的两个细节错误,修正后即可正常绑定文件列表:

1. 修正文件内容的ContentType

测试代码中将文件ContentType设为multipart/form-data是错误的,这是整个表单的类型,而非单个文件的类型。需改为对应文件的实际类型,或通用二进制类型:

// 原错误代码
// fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");

// 修改为通用二进制类型(适配所有文件)
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");

// 若明确文件类型,也可针对性设置,比如文本文件:
// fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("text/plain");

2. 确保表单参数名符合列表绑定规则

ASP.NET Core绑定List<IFormFile>时,只需重复使用相同的参数名即可自动识别为列表。测试代码中参数名xyz与后端一致,无需额外修改,但需确保上述ContentType修正完成。

修改后的测试方法完整代码:

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);
        // 设置正确的文件ContentType
        fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
        // 重复使用参数名"xyz",自动绑定为列表
        formContent.Add(fileContent, "xyz", fileName);
    }

    return formContent;
}

完成上述修改后,后端的xyz参数即可正确接收上传的文件列表,测试会返回200 OK响应。

内容的提问来源于stack exchange,提问作者user1911091

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 09:41:13