Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multipart form POST using ASP.Net Web API

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.

like image 416
badikumar Avatar asked Sep 12 '12 08:09

badikumar


People also ask

How can post data to another page in asp net?

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.

How do I upload a multipart form data?

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.


1 Answers

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.

like image 128
badikumar Avatar answered Sep 20 '22 14:09

badikumar