I have a POST ASP.Net Web Api method adapted from A guide to asynchronous file uploads in ASP.NET Web API RTM.
I am running into faulted Task problem with all the requests fired after the first request is fired and complete.
Here’s the scenario: I have a sample page that posts a file along with other parameters to the Web API Post method. It works fine the first time and the file is uploaded. But, all subsequent requests end up the task in a faulted state. I get “Unexpected end of MIME multipart stream. MIME multipart message is not complete.” Any ideas why?
Pasted below is the source code of my Post method, sample html form and the Aggregate Exception.
public Task<HttpResponseMessage> Post([FromUri]string memberNumber)
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
// Read the form data and return an async task.
var task = Request.Content.ReadAsMultipartAsync(provider).
ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception));
}
return Request.CreateResponse(HttpStatusCode.OK, new MyModel());
});
return task;
}
I am firing this web api using a sample form like this:
<form name="form1" method="post" enctype="multipart/form-data" action="api/claims/asd123" style="margin:auto;width:500px;">
<div>
<label for="HCPracticeNumber">HC Pratice Number:</label>
<input type="text" name="HCPracticeNumber" id="HCPracticeNumber"/>
</div>
<div>
<label for="ServiceDate">Service/Treatment date:</label>
<input type="text" name="ServiceDate" id="ServiceDate"/>
</div>
<div>
<label for="AmountClaimed">Amount Claimed:</label>
<input type="text" name="AmountClaimed" id="AmountClaimed"/>
</div>
<div>
<label for="Image">Image Attachment:</label>
<input name="Image" type="file" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
The AggregateException that is returned is as follows:
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>One or more errors occurred.</ExceptionMessage>
<ExceptionType>System.AggregateException</ExceptionType>
<StackTrace/>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Unexpected end of MIME multipart stream. MIME multipart message is not complete.
</ExceptionMessage>
<ExceptionType>System.IO.IOException</ExceptionType>
<StackTrace>
at System.Net.Http.Formatting.Parsers.MimeMultipartBodyPartParser.<ParseBuffer>d__0.MoveNext() at System.Net.Http.HttpContentMultipartExtensions.MoveNextPart(MultipartAsyncContext context)
</StackTrace>
</InnerException>
</Error>
Update:
After Filip's suggestion on his blog site, I modified the post method to reset the stream position to 0 like this:
Stream reqStream = Request.Content.ReadAsStreamAsync().Result;
if (reqStream.CanSeek)
{
reqStream.Position = 0;
}
var task = Request.Content.ReadAsMultipartAsync(provider).
ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
throw new HttpResponseException(
Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
t.Exception));
}
return Request.CreateResponse(HttpStatusCode.OK, new MyModel());
});
But, this is very temperamental code. It works sometimes it doesn't other times. In other words, it doesn't fix the problem fully.
To post an ASP.NET Web page to another pageAdd a button control to your Web page, such as a Button, LinkButton, or ImageButton control. Set the PostBackUrl property for the control to the URL of the page to which you want to post the ASP.NET Web page.
Multipart form data: The ENCTYPE attribute of <form> tag specifies the method of encoding for the form data. It is one of the two ways of encoding the HTML form. It is specifically used when file uploading is required in HTML form. It sends the form data to server in multiple parts because of large size of file.
It turns out, as Filip suggested in the comments, that the Web API Usage Handler that I adapted from Implementing Message Handlers To Track Your ASP .net Web API Usage was reading the content body and hence messing with the position seeker on the stream when the request was being processed in my POST method.
So, I added a conditional statement to the WebApiUsageHandler to not read the request body if the request is of type IsMimeMultipartContent. This fixed the problem.
Update
I want to update the answer with another option, suggested to me by Filip via email, so that it is documented:
If you use this code inside the API usage handler, just before reading the body:
//read content into a buffer
request.Content.LoadIntoBufferAsync().Wait();
request.Content.ReadAsStringAsync().ContinueWith(t =>
{
apiRequest.Content = t.Result;
_repo.Add(apiRequest);
});
The request will be buffered and will be possible to read it twice, and therefore the upload will be possible further down the pipeline. Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With