我正在使用Visual Studio 2017(15.6.5)中的Asp.Net Core 2.0 Web API技术(完整的.Net Framework 4.6.1)开发REST Web服务。
我的问题
每当我发送超过64 KB的发布请求时,它都会挂起,并且永远不会完成。例如,一个大小为68'259字节的发布请求失败,而一个大小为63'534字节的发布请求确实完成了而没有任何问题。
当我尝试上传图像(使用MultipartFormDataContent
)时,问题首次开始出现,但请求的内容无关紧要。我还尝试将图像作为字节数组和作为转换后的Base64字符串发送到另一个对象实例内的属性中。
到底发生了什么
使用Fiddler捕获流量时,请求永远不会完成。它永远不会收到响应。
甚至不会调用controller方法,因为不会碰到其中的任何断点。但是,我们的自定义中间件的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上传图像。
尝试弄清楚(无济于事)为什么Web服务从未返回响应后,我下载了示例解决方案以查看该解决方案是否仍然有效。它确实做到了,尽管它使用.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日志中没有请求,也没有响应条目。只是在跟踪日志中。
最佳答案
经过消除过程后,我发现了此请求限制的罪魁祸首。
System.Windows.Forms.RichTextBox
我们需要它从RTF文本中提取纯文本,因为我们的Xamarin应用程序无法处理RTF文本。事实证明,ASP.Net Core Web API Web服务不喜欢WinForms组件。
当删除所述RichTextBox的实例化时,图像上载就像它的魅力一样。
经验教训:永远不要在Web服务中使用WinForms组件。
但是,我仍然不知道为什么使用RichTextBox具有这种特定效果(超过64 KB的请求不会得到处理)。
它会触发隐藏的兼容模式吗?是因为缺少UI线程吗?
如果有人知道这一点,请告诉我。
关于c# - 大小超过64KB的请求无法完成(Asp.Net Core Web API),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49672813/