使用ASP.Net Web API进行多部分表单POST请求

10

我有一个POST ASP.Net Web Api方法,它改编自ASP.NET Web API RTM异步文件上传指南

在第一个请求被触发并完成之后,我遇到了所有请求都出现故障的问题。

以下是情况:我有一个示例页面,可以将文件和其他参数一起发布到Web API Post方法。第一次运行正常,文件上传成功。但是,所有后续请求都会导致任务处于出错状态。我得到“意外结束MIME多部分流。MIME多部分消息不完整。”的错误信息。你们有什么想法吗?

下面贴出我的Post方法的源代码、示例html表单和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;
    }

我正在使用类似这样的示例表单来调用此Web API:

<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>

返回的AggregateException如下所示:
<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>

更新:

在Filip的博客建议下,我修改了post方法将流位置重置为0,如下所示:

        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());

            });

但是,这段代码非常不稳定。有时候可以工作,而另一些时候却不能。换句话说,它并没有完全解决问题。


你在其他地方读取请求正文吗?例如MessageHandler?你会如何处理表单中的其他变量?通常这种错误意味着请求正文已经被读取一次,内容流位置已经到达末尾。 - Filip W
你正在使用 Web API RTM 吗? - Filip W
是的,我正在使用Web API RTM。是的,我有一个消息处理程序用于检查HTTP-Method-Override标头,我还有您的WebApiUsage处理程序和另一个身份验证处理程序。我目前还没有对其他参数进行任何操作,但我也会阅读它们。在处理这些问题之前,我首先想让这些东西正常工作。其他未被读取的参数是否与此问题有关? - badikumar
2
该 API 使用处理程序读取请求主体,因此阻止了其他地方对该主体的读取。请注释掉处理程序中读取请求主体的部分,并进行再次测试。 - Filip W
你是绝对正确的。我注释掉了request.Content.ReadAsStringAsync(),现在它完美地工作了。但问题现在变成了,如何让WebApiUsageHandler也记录请求? - badikumar
2个回答

15

正如 Filip 在评论中建议的那样,我从 实现信息处理程序以跟踪您的 ASP .net Web API 使用情况 中适配的 Web API 使用处理程序在读取内容主体时,因此会影响 POST 方法中正在处理请求时流上的位置指针。

因此,我向 WebApiUsageHandler 添加了一个条件语句,以便在请求类型为 IsMimeMultipartContent 时不读取请求主体。这解决了问题。

更新

我想通过电子邮件与 Filip 建议给我另一种选项,以便记录下来:

如果您在 API 使用处理程序中使用此代码,在读取主体之前,请添加以下代码:

   //read content into a buffer
   request.Content.LoadIntoBufferAsync().Wait();

   request.Content.ReadAsStringAsync().ContinueWith(t =>
   {
       apiRequest.Content = t.Result;
       _repo.Add(apiRequest);
   });

这个请求将被缓存,可以读取两次,因此上传将在管道的后面进一步进行。 希望这有所帮助。


添加了下面的代码,但仍然出现未定义的多部分结尾错误: public class CustomWebAPIUsageHandler:DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.Content.IsMimeMultipartContent()) return base.SendAsync(request, cancellationToken); else return Task.Run(() => new HttpResponseMessage(System.Net.HttpStatusCode.UnsupportedMediaType)); } } - Thavudu

2

这不是对原帖提问的答案。然而,调用ReadAsMultipartAsync()方法多次也会导致相同的异常:

public async Task<IHttpActionResult> PostFiles()
{

     // Check if the request contains multipart/form-data.
     if (!Request.Content.IsMimeMultipartContent())
     {
         return Content(HttpStatusCode.BadRequest, "Unsupported media type. ";
    }
     try
     {
        var provider = new CustomMultipartFormDataStreamProvider(workingFolder);

        await Request.Content.ReadAsMultipartAsync(provider); // OK
        await Request.Content.ReadAsMultipartAsync(provider); // calling it the second time causes runtime exception "Unexpected end of MIME multipart stream. MIME multipart message is not complete"
        ... 

    }
    catch(Exception ex)
    {
        ...
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接