Почему падает исключение в методе вызова следующей middleware?
Тот код, что после await next() не вызывается в обоих случаях, так как в 35 строчке падает исключение. Вывод в браузере "To next middleware.To next middleware." Я ожидаю "To next middleware.To next middleware.After next middleware.After next middleware."
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
app.Use(async (ctx, next) =>
{
await ctx.Response.WriteAsync("To next middleware.");
await next(); //28 line
await ctx.Response.WriteAsync("After next middleware.");
});
app.Use(async (ctx, next) =>
{
await ctx.Response.WriteAsync("To next middleware.");
await next(); //35 line
await ctx.Response.WriteAsync("After next middleware.");
});
}
Под отладкой заметил, что в консоли вывода имеется такое сообщение с исключением. Попробовал выставить у ctx.Response.StatusCode это не привело к изменению поведении.
System.InvalidOperationException: StatusCode cannot be set because the response has already started.
at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.ThrowResponseAlreadyStartedException(String name)
at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.set_StatusCode(Int32 value)
at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.set_StatusCode(Int32 value)
at Microsoft.AspNetCore.Http.DefaultHttpResponse.set_StatusCode(Int32 value)
at Microsoft.AspNetCore.Builder.ApplicationBuilder.<>c.<Build>b__18_0(HttpContext context)
at Microsoft.AspNetCore.Builder.UseExtensions.<>c__DisplayClass0_2.<Use>b__2()
at Platform.Startup.<>c.<<Configure>b__1_1>d.MoveNext() in C:\Users\illub\Desktop\learn\ProNetCore3\Ch12_Platform\Platform\Startup.cs:line 35
--- End of stack trace from previous location where exception was thrown ---
at Platform.Startup.<>c.<<Configure>b__1_0>d.MoveNext() in C:\Users\illub\Desktop\learn\ProNetCore3\Ch12_Platform\Platform\Startup.cs:line 28
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()
В чем мое непонимание?
Ответы (2 шт):
Потому что в конце цепочки фреймворк добавляет следующий код:
RequestDelegate app = context =>
{
// …
context.Response.StatusCode = StatusCodes.Status404NotFound;
return Task.CompletedTask;
};
Смысл тут в том, что если вы дошли до конца цепочки - значит, ни один обработчик не "признал" запрос за подлежащий обработке. А значит, нужный адрес не найден и надо возвращать 404. Вот именно этот возврат 404 и выбрасывает исключение, ведь нельзя менять заголовки после записи в Response.
У вас не определен терминальный middleware.
На 35 строчке вызов next() пытается получить его, а так как его нет Asp.Net Core пытается установить StatusCode (404) для ответ. Но StatusCode можно установить только для нового ответа, в который еще ничего не было записано (можно отслеживать по флагу ctx.Response.HasStarted).Так как вы уже начали туда писать, строка 27 ctx.Response(...), ответ помечается запущенным (ctx.Response.HasStarted = true) и при попытке установить StatusCode происходит исключение.
Определить терминальный middleware можно, к примеру, так:
app.Run(context => Task.CompletedTask);
Но учтите, что вызывать await ctx.Response.WriteAsync(...) после вызова await next() категорически не рекомендуется, вы можете записать в ответ больше данных чем будет указано в заголовках 'Content-Length', что может привести к неверной трактовке содержимого на клиенте (он просто вычитает часть ответа).
Дополнительную информацию можете получить по ссылке