大小超过64KB的请求无法完成(Asp.Net Core Web API)

时间:2018-04-05 12:47:57

标签: c# rest image-uploading asp.net-core-webapi .net-4.6.1

我正在使用Visual Studio 2017(15.6.5)中的Asp.Net Core 2.0 Web API技术(完整的.Net Framework 4.6.1)开发REST Web服务。

我的问题

每当我发送超过64 KB的发布请求时,它都会挂起并且永远不会完成。例如,大小为68'259字节的post请求失败,而大小为63'534字节的post请求确实完成没有任何问题。

当我尝试上传图片(使用MultipartFormDataContent)时问题首次出现时,请求的内容无关紧要。我还尝试将图像作为字节数组和转换后的Base64字符串发送到另一个对象实例中的属性中。

究竟发生了什么

使用Fiddler捕获流量时,请求永远不会完成。它永远不会收到回应。

Request log entry

Request details

控制器方法永远不会被调用,因为它内部没有任何断点。但是,我们调用自定义中间件的Invoke方法,但它永远不会超过await this._next.Invoke(context)行。

public class VersioningMiddleware
{
    private readonly RequestDelegate _next;

    public VersioningMiddleware(RequestDelegate next)
    {
        this._next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        ...

        await this._next.Invoke(context);
    }
    ...
}

更多信息

我使用this tutorial上传图片。

在试图找出(无效)为什么webservice永远不会返回响应时,我下载了示例解决方案,看看是否有效。虽然它使用了.Net Standard而不是完整的.Net Framework,但它确实使用完整的.Net Framework在该解决方案中创建了一个新的Web服务项目,复制了整个控制器并将目标平台更改为x86以匹配配置到我的实际Web服务(我们仍然需要查询MS Access数据库,这就是为什么我坚持使用32位应用程序)。 这个新版本的示例Web服务也像上传大于64 KB的图像一样迷人。

这对我来说没什么意义,因为我看不出任何明显的差异。

发送请求的应用代码(为了清晰起见,在某些部分中缩写):

public async Task UploadImageAsync(byte[] imageData, string fileName, object objectParameter, CancellationToken cancellationToken, params string[] pathComponents)
{
    ...

    using (MemoryStream stream = new MemoryStream(imageData))
    {
        using (HttpContent fileStreamContent = new StreamContent(stream))
        {
            fileStreamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "file", FileName = fileName };
            fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            using (MultipartFormDataContent formData = new MultipartFormDataContent())
            {
                formData.Add(fileStreamContent);
                string apiPath = String.Join(Globals.PathSeparator, pathComponents);
                if (objectParameter != null)
                {
                    apiPath = apiPath + "?" + objectParameter.ToQueryString();
                }
                await this.SendPostAsync(apiPath, formData, cancellationToken).ConfigureAwait(false))
            }
        }
    }
} 

private Task<HttpResponseMessage> SendPostAsync(string path, HttpContent content, CancellationToken cancellationToken)
{
    return this.SendRequestAsync(HttpAction.Post, path, content, cancellationToken);
} 

private async Task<HttpResponseMessage> SendRequestAsync(HttpAction action, string path, HttpContent content, CancellationToken cancellationToken)
{
    ...
    switch (action)
    {
        ...
        case HttpAction.Post:
        response = await this._client.PostAsync(path, content, cancellationToken).ConfigureAwait(false);
        break;
        ...
    }
...
return response;
}

Web服务控制器代码(也简称):

[Route("api/[controller]")]
public class DocumentsController : ControllerBase
{ 
    private readonly IDocumentsRepository _documents; 
    ...

    public DocumentsController(IDocumentsRepository documents, ...)
    {
        this._documents = documents;
        ...
    }
    ...

    [HttpPost(nameof(SaveImage))]
    [Authorize(JwtBearerDefaults.AuthenticationScheme)]
    public IActionResult SaveImage(IFormFile file, [FromQuery]ServiceReportIdentification documentInfo)
    {
        this._documents.SaveImage(file, documentInfo);
        return this.CreatedAtAction(nameof(SaveImage), file);
    }
    ...
}

请求大小限制

默认的IIS请求大小限制应该是4 MB,我尝试上传的大多数图像都不到1 MB,所以这应该不是问题。但是,我仍然尝试手动设置我可以找到的所有大小限制,看看是否可以覆盖隐藏的64 KB大小限制。没有骰子。

我甚至从示例解决方案的.vs \ config \ applicationhost.config文件中复制了httpCompression标记,这是我和实际Web服务解决方案之间的唯一真正区别。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <httpRuntime maxRequestLength="10240" />
  </system.web>
  <system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="10485760" />
      </requestFiltering>
    </security>
    <handlers>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" startupTimeLimit="3600" requestTimeout="23:00:00" />
    <httpCompression>
        <dynamicCompression>
            <add mimeType="text/event-stream" enabled="false" />
        </dynamicCompression>
    </httpCompression>
  </system.webServer>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="ServicePlusApi" maxReceivedMessageSize="10485760" />
            </wsHttpBinding>
        </bindings>
    </system.serviceModel>
</configuration>

一些背景

当我创建Web服务解决方案时,Asp.Net Core尚未达到2.0版本,这就是为什么Web API项目中仍然存在web.config文件的原因。从2.0开始(或者至少是我的假设),web.config文件不再存在。相反,默认配置存放在.vs \ config \ applicationhost.config文件中。

出于这个原因,我创建了一个全新的解决方案和Web API项目,添加到我的Web服务的代码中以及引用所需的其他项目中,实质上是使用相同的代码库重新创建整个解决方案,但最新的项目结构/定义。

然而,这并没有飞过。

我不知道导致奇怪的64 KB请求大小限制的原因是什么,它甚至没有返回错误,IIS日志中也没有任何记录请求。它只是吞下请求,从不提供任何类型的响应(除了客户端超时)。

有谁知道这可能是什么原因?我已经在互联网上搜索了很长一段时间,除了那些在这种情况下不起作用的手动大小限制覆盖,我发现没有任何帮助。

修改

上述行为在使用Visual Studio的IIS Express的调试模式下在开发机器上本地发生。当Web服务以发布模式发布并由另一台计算机上的标准IIS托管时,同样适用。

更新

在搜索IIS跟踪日志以获取更多线索时,我发现了以下事件:

<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
 <System>
  ...
 </System>
 <EventData>
  <Data Name="ContextId">{8000006B-0006-FF00-B63F-84710C7967BB}</Data>
  <Data Name="ModuleName">AspNetCoreModule</Data>
  <Data Name="Notification">128</Data>
  <Data Name="HttpStatus">400</Data>
  <Data Name="HttpReason">Bad Request</Data>
  <Data Name="HttpSubStatus">0</Data>
  <Data Name="ErrorCode">2147952454</Data>
  <Data Name="ConfigExceptionInfo"></Data>
 </EventData>
 <RenderingInfo Culture="de-CH">
  <Opcode>MODULE_SET_RESPONSE_ERROR_STATUS</Opcode>
  <Keywords>
   <Keyword>RequestNotifications</Keyword>
  </Keywords>
  <freb:Description Data="Notification">EXECUTE_REQUEST_HANDLER</freb:Description>
  <freb:Description Data="ErrorCode">Eine vorhandene Verbindung wurde vom Remotehost geschlossen.
 (0x80072746)</freb:Description>
 </RenderingInfo>
 ...
</Event>

错误描述是德语,并说:“现有连接已被远程主机关闭。”

再远一点,我发现了一些响应数据,其中包含错误页面的缓冲区数据(由于Web服务的性质而从未显示),包含以下错误文本:“请求无法理解服务器由于语法错误。“

奇怪的是,正常的IIS日志中还没有请求,也没有响应条目。只是在跟踪日志中。

1 个答案:

答案 0 :(得分:2)

在完成淘汰过程后,我发现了这个请求限制的罪魁祸首。

System.Windows.Forms.RichTextBox

我们需要它从RTF文本中提取纯文本,因为我们的Xamarin应用程序无法处理RTF文本。事实证明,ASP.Net Core Web API Web服务不喜欢WinForms组件。

当删除所述RichTextBox的实例化时,图像上传就像魅力一样,就像它应该的那样。

获得的经验教训:永远不要在Web服务中使用WinForms组件。

但是,我仍然不知道为什么使用RichTextBox会产生这种特殊效果(超过64 KB的请求不会被处理)。

是否会触发隐藏的兼容模式?是因为缺少UI线程吗?

如果有人知道,请告诉我。