I am trying to upload the image without a filename. The API received null in the file parameter.
The Request Send code (client side):
var content = new MultipartFormDataContent
{
{ new StreamContent(file), "file" },
{ new StringContent(JsonConvert.SerializeObject(metadata), Encoding.UTF8, "application/json"), "metadata" }
};
Request Receive code (API With .Net 8):
var form = await Request.ReadFormAsync();
var formFile = form.Files["file"];
//The formFile is returned as NULL
If I supply the filename in the stream content then it does work. But my requirement is to make it work without providing the filename.
I've found the solution, we need to change the api code as:
// Create a minimal API endpoint that handles a POST
// We use the default configured JsonOptions for deserializing the JSON
app.MapPost("/", async (HttpContext ctx, IOptions<JsonOptions> jsonOptions) =>
{
// make sure we have the correct header type
if (!MediaTypeHeaderValue.TryParse(ctx.Request.ContentType, out MediaTypeHeaderValue? contentType)
|| !contentType.MediaType.Equals("multipart/form-data", StringComparison.OrdinalIgnoreCase))
{
return Results.BadRequest("Incorrect mime-type");
}
// Variables for holding the data parsed from the response
MyData? jsonData = null;
byte[]? binaryData = null;
// Get the multipart/form-boundary header from the content-type
// Content-Type: multipart/form-data; boundary="--73dc24e0-b350-48f8-931e-eab338df00e1"
// The spec says 70 characters is a reasonable limit.
string boundary = GetBoundary(contentType, lengthLimit: 70);
var multipartReader = new MultipartReader(boundary, ctx.Request.Body);
// Use the multipart reader to read each of the sections
while (await multipartReader.ReadNextSectionAsync(ct) is { } section)
{
// Make sure we have a content-type for the section
if(!MediaTypeHeaderValue.TryParse(section.ContentType, out MediaTypeHeaderValue? sectionType))
{
return Results.BadRequest("Invalid content type in section " + section.ContentType);
}
if (sectionType.MediaType.Equals("application/json", StringComparison.OrdinalIgnoreCase))
{
// If the section is JSON, deserialize directly from the section stream
// using the default JSON serialization options configured for the app
jsonData = await JsonSerializer.DeserializeAsync<MyData>(
section.Body,
jsonOptions.Value.JsonSerializerOptions,
cancellationToken: ctx.RequestAborted);
}
else if (sectionType.MediaType.Equals("application/octet-stream", StringComparison.OrdinalIgnoreCase))
{
// If the section is binary data, deserialize into an array
// there are potentially more efficient things we could do here
// depending on how you need the data
using var ms = new MemoryStream();
await section.Body.CopyToAsync(ms, ctx.RequestAborted);
binaryData = ms.ToArray();
}
else
{
return Results.BadRequest("Invalid content type in section " + section.ContentType);
}
}
// Just printing it out for debugging purposes
app.Logger.LogInformation("Receive Json {JsonData} and binary data {BinaryData}",
jsonData, Convert.ToBase64String(binaryData));
return Results.Ok();
// Retrieves the boundary marker from the content-type, handling quotes etc
// Taken from https://github.com/dotnet/aspnetcore/blob/4eef6a1578bb0d8a4469779798fe9390543d15c0/src/Http/Http/src/Features/FormFeature.cs#L318-L320
static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
{
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary);
if (StringSegment.IsNullOrEmpty(boundary))
{
throw new InvalidDataException("Missing content-type boundary.");
}
if (boundary.Length > lengthLimit)
{
throw new InvalidDataException($"Multipart boundary length limit {lengthLimit} exceeded.");
}
return boundary.ToString();
}
});
Source of code : https://andrewlock.net/reading-json-and-binary-data-from-multipart-form-data-sections-in-aspnetcore/