skip to Main Content

I want to create a simple file upload endpoint in ASP.NET Core 6 and thought it would be as easy as described here https://dotnetthoughts.net/handling-file-uploads-in-openapi-with-aspnet-core/.

When I have an endpoint defined like:

app.MapPost("/upload", (IFormFile file) =>
{
    //Do something with the file
    return Results.Ok();
}).Accepts<IFormFile>("multipart/form-data").Produces(200);

I get a 415 back when I call the endpoint. The message I get back is something like:

Expected a supported JSON media type but got "multipart/form-data; …

Not sure why it expected a supported json when I say that the endpoint should accept multipart/form-data.

Any ideas or thoughts on what to do here?

4

Answers


  1. Currently out of the box support for binding in Minimal APIs is quite limited. Supported binding sources:

    • Route values
    • Query string
    • Header
    • Body (as JSON)
    • Services provided by dependency injection
    • Custom

    NOTE: Binding from forms is not natively supported in .NET 6

    You can either leverage custom binding or use special types handling:

    app.MapPost("/upload", (HttpRequest request) =>
    {
        //Do something with the file
        var files = request.Form.Files;
        return Results.Ok();
    })
    .Accepts("multipart/form-data")
    .Produces(200);
    

    UPD

    Since Minimal APIs should be able to bind IFormFile/IFormFileCollection directly:

    app.MapPost("/upload", async (IFormFile file) =>
    {
        // ...
    });
    
    app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
    {
        foreach (var file in myFiles)
        {
            // ...
        }
    });
    
    Login or Signup to reply.
  2. Just noting here, you can upload a file with any ContentType like the following sample code.

    In this example I chose a text/plain ContentType,
    but you can choose your own by editing the .Accepts<IFormFile>("text/plain"); line to your desired ContentType.

    app.MapPost("/upload",
        async (HttpRequest request) =>
        {
            using (var reader = new StreamReader(request.Body, System.Text.Encoding.UTF8))
            {
    
                // Read the raw file as a `string`.
                string fileContent = await reader.ReadToEndAsync();
    
                // Do something with `fileContent`...
        
                return "File Was Processed Sucessfully!";
            }
        }).Accepts<IFormFile>("text/plain");
    

    It is also well supported by Swagger UI:

    enter image description here

    Login or Signup to reply.
  3. app.MapPost("/upload",
        async Task<IResult> (HttpRequest request) =>
        {
            if (!request.HasFormContentType)
                return Results.BadRequest();
    
            var form = await request.ReadFormAsync();
    
            if (form.Files.Any() == false)
                return Results.BadRequest("There are no files");
    
            var file = form.Files.FirstOrDefault();
    
            if (file is null || file.Length == 0)
                return Results.BadRequest("File cannot be empty");
    
            using var stream = file.OpenReadStream();
    
            var reader = new StreamReader(stream);
            var text = await reader.ReadToEndAsync();
    
            return Results.Ok(text);
        }).Accepts<IFormFile>("multipart/form-data"); 
    
    Login or Signup to reply.
  4. This has been addressed with .NET 7. Support for IFormFile and IFormFileCollection has been added. You should be able to use it in your MediatR pipelines.

    Ref: .NET 7 Minimal API Support for File Upload Bindings

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.MapGet("/", () => "Hello World!");
    
    app.MapPost("/upload", async (IFormFile file) =>
    {
        var tempFile = Path.GetTempFileName();
        app.Logger.LogInformation(tempFile);
        using var stream = File.OpenWrite(tempFile);
        await file.CopyToAsync(stream);
    });
    
    app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
    {
        foreach (var file in myFiles)
        {
            var tempFile = Path.GetTempFileName();
            app.Logger.LogInformation(tempFile);
            using var stream = File.OpenWrite(tempFile);
            await file.CopyToAsync(stream);
        }
    });
    
    app.Run();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search