-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
Describe the bug
I have a C# API endpoint that accepts files for processing, defined like so:
//POST api/Ticket/Attachment
[HttpPost("Attachment")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> PostAttachment(int ticketId, [FromForm]IFormFileCollection files)
When I call this endpoint from Postman, the endpoint successfully receives / processes the files.
However, I receive an InvalidDataException: Multipart body length limit 16384 exceeded. exception when I call the endpoint from a C# client using this code:
public async Task PostAttachmentAsync(int ticketID, IFormFileCollection files, CancellationToken cancellationToken = default)
{
using (HttpRequestMessage request = new HttpRequestMessage())
{
string boundary = $"{Guid.NewGuid().ToString()}";
MultipartFormDataContent requestContent = new MultipartFormDataContent(boundary);
requestContent.Headers.Remove("Content-Type");
// The two dashes in front of the boundary are important as the framework includes them when serializing the request content.
requestContent.Headers.TryAddWithoutValidation("Content-Type", $"multipart/form-data; boundary=--{boundary}");
foreach (IFormFile file in files)
{
StreamContent streamContent = new StreamContent(file.OpenReadStream());
requestContent.Add(streamContent, file.Name, file.FileName);
streamContent.Headers.ContentDisposition.FileNameStar = "";
}
request.Content = requestContent;
request.Method = new HttpMethod("POST");
request.RequestUri = new Uri($"api/v3/Services/WorkItem/Ticket/Attachment?ticketID={ticketID}", UriKind.Relative);
HttpClient client = await CreateHttpClientAsync(cancellationToken).ConfigureAwait(false);
using (HttpResponseMessage response =
await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false)
)
{
if (response == null)
throw new PlatformAPIClientException("Could not retrieve a response.");
Dictionary<string, IEnumerable<string>> responseHeaders = Enumerable.ToDictionary(response.Headers, h => h.Key, h => h.Value);
if (response.Content?.Headers != null)
{
Dictionary<string, IEnumerable<string>> contentHeaders = Enumerable.ToDictionary(response.Content.Headers, h => h.Key, h => h.Value);
responseHeaders.Concat(contentHeaders);
}
int responseStatusCode = (int)response.StatusCode;
switch (responseStatusCode)
{
case StatusCodes.Status200OK:
case StatusCodes.Status204NoContent:
return;
case StatusCodes.Status400BadRequest:
string badRequestResponse = response.Content == null ? "No Response Content" :
await response.Content.ReadAsStringAsync().ConfigureAwait(false);
ValidationProblemDetails validationResult = default;
try
{
validationResult = JsonConvert.DeserializeObject<ValidationProblemDetails>(badRequestResponse, _settings.Value);
}
catch(Exception ex)
{
throw new PlatformAPIClientException("Could not deserialize the response body.", responseStatusCode, badRequestResponse,
responseHeaders, ex);
}
throw new PlatformAPIClientException<ValidationProblemDetails>("A server side error occurred.", responseStatusCode,
badRequestResponse, responseHeaders, validationResult, innerException: null);
case StatusCodes.Status401Unauthorized:
case StatusCodes.Status403Forbidden:
case StatusCodes.Status500InternalServerError:
string errorResponse = response.Content == null ? "No Response Content" :
await response.Content.ReadAsStringAsync().ConfigureAwait(false);
ProblemDetails problemResult = default;
try
{
problemResult = JsonConvert.DeserializeObject<ProblemDetails>(errorResponse, _settings.Value);
}
catch(Exception ex)
{
throw new PlatformAPIClientException("Could not deserialize the response body.", responseStatusCode, errorResponse,
responseHeaders, ex);
}
throw new PlatformAPIClientException<ProblemDetails>("A server side error occurred.", responseStatusCode,
errorResponse, responseHeaders, problemResult, innerException: null);
default:
string unexpectedResponse = response.Content == null ? "No Response Content" :
await response.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new PlatformAPIClientException($"The HTTP status code ({responseStatusCode}) of the response was not expected.",
responseStatusCode, unexpectedResponse, responseHeaders, innerException: null);
}
}
}
}
Google / Stack Overflow suggest that the exception should be resolved by using either the attribute [RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue)] or configured globally in Startup:
services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = long.MaxValue;
});
However, neither suggested solution prevents the InvalidDataException from being thrown when the endpoint is called by the C# client.
Can you please provide guidance as to whether this is a bug in the framework or my C# client code?
To Reproduce
Steps to reproduce the behavior:
- Using this version of ASP.NET Core '2.2'
Expected behavior
The C# client successfully posts the request without triggering the InvalidDataException.
Additional context
dotnet-info.txt
testfile.jpg
successful-postman-request.txt
failed-csharp-request.txt