Search code examples
c#restasp.net-coremultipartform-dataasp.net-core-6.0

.NET 6 Core C# -building a rest webservice with binary multipart/form-data upload returning json


I'm trying to build a .NET 6.0 C# rest webservice, receiving a string and three files, one of them a binary image. The files are not saved, just checked/evaluated and the result is returned in a json structure. The string can be an url path parameter. The webservice should run on windows and in a linux docker container. It is called only from C# clients and is not accessible from the internet.

So the curl looks like that:

curl -X POST -H "Content-Type: multipart/form-data; boundary=------------------------d74496d66958873e" \
--data-binary "@KeyValue.json" \
--data-binary "@Text.txt" \
--data-binary "@Image.tif" \
http://localhost:5000/check/CheckType01

I tried an approach starting with the Visual Studio 2022 Project "ASP.NET Core-Web-Api"

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

app.MapPost("/check/{checkType}", (string checkType, HttpContext ctx) =>
{
    ctx.Request.EnableBuffering();
    ctx.Request.Body.Position = 0;
    var reader = new StreamReader(ctx.Request.Body);
    var body = Task.Run(async () => await reader.ReadToEndAsync().ConfigureAwait(false));

    Console.WriteLine("Parameter:  " + checkType);
    Console.WriteLine("MultiPart:  " + body.Result);

    return new { check = "Ok", confidence = 100 }; ;
});

app.Run();

With the three files containing "KeyValueContent", "TextContent" and "ImageContent", this code partially works:

Output:
Parameter:  CheckType01
MultiPart:  KeyValueContent&TextContent&ImageContent
Return:
{"check":"Ok","confidence":100}

But this can't be the correct approach to solve this, I assume.

  • The files are separated by an '&' and not by the boundary from the header, so it is not possible to separate them.
  • Somehow the framework should do the separation instead.
  • The multipart body is a string and not a byte array.
  • The webservice is blocked during the body read.

I have read a lot about how to upload files in ASP.Net Core, about webapi-controllers, model view controllers, annotations, minimal API or not and so on, but I'm still not sure what the correct way to go is, I couldn't find a working minimal example.

I would like to know:

  • which Visual Studio 2022 starting project type should I use?
  • what framework to use (minimal api, api with controllers, [ApiController] attribute ...)?
  • the necessary attributes for POST - binary multipart/form-data?

Solution

  • Besides fixing the curl request to read multiple file you can use IFormFileCollection returned by HttpRequest.Form.Files (inject HttpRequest as handler parameter or use ctx.Request to access it).

    Also note that Task.Run not only pointless here but harmful, just mark the lambda handler as async:

    app.MapPost("/check/{checkType}", async (string checkType, HttpRequest request) =>
    {
        var formFileCollection = request.Form.Files;
        foreach (var formFile in request.Form.Files)
        {
            using var openReadStream = new StreamReader(formFile.OpenReadStream());
            var readToEndAsync = await openReadStream.ReadToEndAsync();
            // do something here
        }
    
        ...
    
        return new { check = "Ok", confidence = 100 };
    });