Skip to content

Uploading files with ASP.NET Minimal APIs

With the .net 6 release, I was extremely excited to play with Minimal APIs.

One of the first things that I've done was to move an existing project to .net 6 and convert my Controllers into a Minimal API.

I've found the process easy, but I was surprised when the tests for an Upload endpoint failed.

My implementation after converting into Minimal API Endpoint was this. 👇

app.MapPost("/upload",
async Task<IResult>(IFormFile request) =>
{
if (request.Length == 0)
return Results.BadRequest();

await using var stream = request.OpenReadStream();

var reader = new StreamReader(stream);
var text = await reader.ReadToEndAsync();

return Results.Ok(text);
});

And the test this. 👇

await using var application = new Application();
using var client = application.CreateClient();

using var formData = new MultipartFormDataContent();
await using var file = File.OpenRead("text.txt");
var streamContent = new StreamContent(file);
formData.Add(streamContent, "file", "text.txt");

var response = await client.PostAsync("/upload",
formData);

response.StatusCode.Should().Be(HttpStatusCode.OK);
var data = await response.Content.ReadAsStringAsync();
data.Should().Be("\"Hello World!\"");

The expected 200 OK status code was now a 415 Unsupported Media Type 🤔

That was strange. My first thought was that I was missing to define the content type accepted by that endpoint.

.Accepts<IFormFile>("multipart/form-data");

I realized I was stupid by thinking that since the code above is just about adding OpenAPI Metadata. 😅

Then, finally, I got it. Minimal APIs will try to bind attributes with the assumption that content is JSON.

So, how do I handle it?

I had to receive the HttpRequest request as an argument.

Then, I was able to read the Form and look for files.

app.MapPost("/upload",
async Task<IResult>(HttpRequest request) =>
{
if (!request.HasFormContentType)
return Results.BadRequest();

var form = await request.ReadFormAsync();
var formFile = form.Files["file"];

if (formFile is null || formFile.Length == 0)
return Results.BadRequest();

await using var stream = formFile.OpenReadStream();

var reader = new StreamReader(stream);
var text = await reader.ReadToEndAsync();

return Results.Ok(text);
});

In the end, it requires a lit bit of extra effort, but it's not a big deal. Maybe in a future version, we may have a simple way of accomplishing it.

I hope that this was useful! To get more tips like this, follow me on Twitter (@gsferreira) and let's keep in touch!