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

Request with size over 64KB fails to complete (Asp.Net Core Web API)

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

我的问题

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

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

到底发生了什么

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

控制器方法从未被调用过,因为其中没有断点被击中。然而,我们的自定义中间件的 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 上传图片。

在试图找出(无济于事)为什么网络服务从未 return 响应时,我下载了示例解决方案以查看该解决方案是否有效。它做到了,尽管它使用 .Net Standard 而不是完整的 .Net Framework,所以我在该解决方案中使用完整的 .Net Framework 创建了一个新的 webservice 项目,复制了整个控制器并将目标平台更改为 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 服务的代码以及引用所需的其他项目,本质上是使用相同的代码库重新创建整个解决方案但是最新项目 structure/definition.

不过,这并没有成功。


我不知道是什么导致了奇怪的 64 KB 请求大小限制,这甚至没有 return 错误,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>

错误描述是德语的:"An existing connection has been closed by the remote host."

再往下一点,我发现了一些响应数据,其中包含错误页面的缓冲区数据(由于网络服务的性质,它永远不会显示),包含以下错误文本:"The request could not be understood by the server due to malformed syntax."

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

经过排除,我找到了这个请求限制的罪魁祸首

System.Windows.Forms.RichTextBox

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

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

经验教训:切勿在 Web 服务中使用 WinForms 组件。


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

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

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