简单的 ASP.NET Core 中间件项目中的“响应已开始”异常

McG*_*V10 4 c# asp.net-core asp.net-core-middleware

我的问题基本上是这样的 - 鉴于此处显示的代码,管道中还运行什么来尝试启动对客户端的响应?我知道有关该异常的其他问题,但在我的中间件导致异常之后运行的管道中似乎有一些东西引起了异常——它不是由我的中间件引起的,我认为这是我的场景中的差异。

这是一个简单的 ASP.NET Core 3.0 WebSocket 回显服务器——没有 SignalR、没有 MVC、没有路由、没有静态页面支持等。除了处理套接字之外,当中间件看到一个请求时,它会发回一个简单的text/html请求page(硬编码字符串)作为回显客户端。

The browser receives the content just fine, and my exception handler is not triggered (a crucial point), but after my middleware is done processing the request, ASP.NET Core logs the exception:

StatusCode cannot be set because the response has already started.

The code is quite minimal:

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>();
    }
}
Run Code Online (Sandbox Code Playgroud)
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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

I thought the problem might be my use of WriteAsync but it seems to happen if I also set the HTTP status elsewhere, with no other output, like setting HTTP 500 in the catch block. If I step through a purposely-caused exception by adding a throw as the very first statement in the middleware, it gets to the finally where the "already started" exception occurs.

So what else tries to produce output in the pipeline given that Startup class?

Edit: Stack trace, line 91 referenced at the end is the await next(context) in the finally block.

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 context) at KestrelWebSocketServer.WebSocketMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\Source\WebSocketExample\KestrelWebSocketServer\WebSocketMiddleware.cs: Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass5_1 的第 91 行。 <b__1>d.MoveNext() --- 从先前抛出异常的位置开始的堆栈跟踪结束 --- 在 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1应用)

McG*_*V10 7

用户@Nkosi 的评论是正确的——当我的中间件能够完全处理请求时(将 HTTP 升级到 WS,或者发送回 echo 客户端 HTML),它不应该将上下文交给委托next。然而,在这个非常简单的示例中,其他请求(例如浏览器尝试检索图标)我的中间件没有执行任何操作,因此解决方案是这样的:

finally
{
    if(!context.Response.HasStarted)
        await next(context);
}
Run Code Online (Sandbox Code Playgroud)

  • [此处](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/index?view=aspnetcore-2.2) 文档中多次提到它作为 **短路** 。还有一个关于在响应发送到客户端后不调用“next.Invoke”的特定警告。 (2认同)