【发布时间】:2019-08-18 13:28:41
【问题描述】:
我的问题基本上是这样的——鉴于此处显示的代码,管道中还有什么正在尝试启动对客户端的响应?我知道有关该异常的其他问题,但似乎在我的中间件之后运行的管道中的某些东西导致了异常 - 它不是由我的中间件引起的,我认为是我的方案的不同之处。
这是一个简单的 ASP.NET Core 3.0 WebSocket 回显服务器——没有 SignalR、没有 MVC、没有路由、没有静态页面支持等。除了处理套接字之外,当中间件看到对 @987654323 的请求时@ 它作为回显客户端发回一个简单的页面(一个硬编码的字符串)。
浏览器接收到内容就好了,我的异常处理程序没有触发(关键点),但是在我的中间件处理完请求后,ASP.NET Core 记录了异常:
无法设置状态码,因为响应已经开始。
代码非常少:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<WebSocketMiddleware>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
ReceiveBufferSize = 4 * 1024
};
app.UseWebSockets(webSocketOptions);
app.UseMiddleware<WebSocketMiddleware>();
}
}
public class WebSocketMiddleware : IMiddleware
{
// fields/properties omitted
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
try
{
if (context.WebSockets.IsWebSocketRequest)
{
// omitted, socket upgrade works normally
}
else
{
if(context.Request.Headers["Accept"][0].Contains("text/html"))
{
// this works but causes the exception later in the pipeline
await context.Response.WriteAsync(SimpleHtmlClient.HTML);
}
else
{
// ignore other requests such as favicon
}
}
}
catch (Exception ex)
{
// code omitted, never triggered
}
finally
{
// exception happens here
await next(context);
}
}
}
我认为问题可能是我使用了WriteAsync,但如果我还在其他地方设置了 HTTP 状态,没有其他输出,比如在catch 块中设置 HTTP 500,它似乎就会发生。如果我通过添加throw 作为中间件中的第一条语句来逐步处理故意引起的异常,它会到达finally,在该处发生“已启动”异常。
那么考虑到Startup 类,还有什么尝试在管道中产生输出?
编辑:堆栈跟踪,最后引用的第 91 行是 finally 块中的 await next(context)。
System.InvalidOperationException:无法设置 StatusCode,因为响应已经开始。 在 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ThrowResponseAlreadyStartedException(字符串值) 在 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.set_StatusCode(Int32 值) 在 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.set_StatusCode(Int32 值) 在 Microsoft.AspNetCore.Http.DefaultHttpResponse.set_StatusCode(Int32 值) 在 Microsoft.AspNetCore.Builder.ApplicationBuilder.c.b__18_0(HttpContext 上下文) 在 C:\Source\WebSocketExample\KestrelWebSocketServer\WebSocketMiddleware.cs:line 91 中的 KestrelWebSocketServer.WebSocketMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) 在 Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.c__DisplayClass5_1.
d.MoveNext() --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 应用程序)
【问题讨论】:
-
一个问题,你为什么要向服务提供商注册中间件?以前从未见过有人这样做。您是否尝试过从配置服务委托中删除它? docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/…
-
这就是他们所说的“工厂激活的中间件”。我个人不喜欢“魔术方法”,宁愿让我的中间件显式地实现一个接口(在这种情况下为
IMiddleware)。 docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/… -
一旦您写入流,其他中间件将无法写入。如果您想写入并通过管道传递一个流,则需要替换流。
-
你为什么还要顺流而下。如果您正在写入响应,您的意思不是要使管道短路吗?
-
框架将添加自己的处理程序。在这种情况下,您需要在下次调用时进行更改。如果您正在写入响应,则应在此处结束它,而不是在管道中调用下一个。
标签: c# asp.net-core asp.net-core-middleware