Reputation: 23472
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?
Upvotes: 15
Views: 31553
Reputation: 143511
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 .net-7.0 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)
{
// ...
}
});
Upvotes: 18
Reputation: 71
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();
Upvotes: 6
Reputation: 36070
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");
Upvotes: 0
Reputation: 655
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:
Upvotes: 9