Я работаю над веб-приложением .NET 8. Я внедрил промежуточное программное обеспечение для регистрации запросов и ответов, но столкнулся с проблемой, когда код состояния неожиданно меняется с 200 на 204 после вызова _next(context). Вот код и подробное описание моей настройки и проблемы:
public async Task Invoke(HttpContext context)
{
var requestBody = await GetRequestBody(context.Request);
var originalBodyStream = context.Response.Body;
using (var memoryStream = new MemoryStream())
{
context.Response.Body = memoryStream;
// Log initial headers and status code: StatusCode is 200 before request goes to handle.
Console.WriteLine($"Initial Status Code: {context.Response.StatusCode}");
await _next(context);
// Log headers and status code after _next. StatusCode is 204 even though its is being set to 200 explicitly in handler and context.Response.Body is also not null/empty.
Console.WriteLine($"Status Code After _next: {context.Response.StatusCode}");
memoryStream.Seek(0, SeekOrigin.Begin);
var responseBody = await new StreamReader(memoryStream).ReadToEndAsync();
context.Response.Body = originalBodyStream;
// Write the response body if it is not empty
if (!string.IsNullOrEmpty(responseBody))
{
try
{
context.Response.ContentLength = Encoding.UTF8.GetByteCount(responseBody);
//While writing responseBody it also throws following exception: Writing to the response body is invalid for responses with status code 204.
await context.Response.WriteAsync(responseBody);
}
catch (Exception ex)
{
Console.WriteLine($"Error writing response body: {ex.Message}");
}
}
await memoryStream.CopyToAsync(originalBodyStream);
}
}
public class CreateAuthorization : Endpoint<CreateAuthorizationRequest, CreateAuthorizationResponse>
{
public override void Configure()
{
Post(CreateAuthorizationRequest.Route);
Summary(s =>
{
s.Summary = "Create a new Authorization request.";
s.Description = "Create a new Authorization request to get random number from Nafath.";
});
}
public override async Task HandleAsync(CreateAuthorizationRequest request, CancellationToken cancellationToken)
{
var result = await _mediator.Send(new CreateAuthorizationCommand(request.IdNumber!, action, request.Service!, ""), cancellationToken);
var convertedResult = result.IsSuccess ? new CreateAuthorizationResponse(result.Value.TransactionId, result.Value.Random) : Result.Error();
await convertedResult.ToOkOrProblemDetail(HttpContext);
//Till this point HttpContext.Response.StatusCode is 200 and after this it goes directly to middleware after the line `await _next(context);` and somehow status code changed to 204.
}
}
public static async Task ToOkOrProblemDetail(this Result result, HttpContext httpContext)
{
if (result.IsSuccess)
{
var responseBody = JsonSerializer.Serialize(result.Value);
httpContext.Response.StatusCode = StatusCodes.Status200OK;
httpContext.Response.ContentType = "application/json";
httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(responseBody);
await httpContext.Response.WriteAsync(responseBody);
Console.WriteLine("Response written with status 200 and content length set");
}
else
{
await result.ToProblemDetails(httpContext);
}
}
В Program.cs
промежуточное программное обеспечение настроено следующим образом:
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app. UseDeveloperExceptionPage() ;
app.UseShowAIIServicesMiddIeware();
}
else
{
app.UseDefauItExceptionHandIer();
app.UseHsts();
}
app.UseAuthentication();
app.UseAuthorization();
app.UseFastEndpoints(x => x.Errors.UseProbIemDetails());
//Using the middleware right after configuration of FastEndpoints but moving this to right after builder.Build() or right before app.Run() have not affect on the error.
app.UseMiddleware<RequestLoggingMiddleware>();
app.UseSwaggerGen();
app.UseHttpsRedirection();
SeedDatabase(app);
if (GetLocaIIPAddress() == && builder.Configuration.GetValue<string>("AllowIp"))
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new AllowAnonymousAccessFilter() }
});
app.Run();
Несмотря на то, что код состояния установлен на 200 в обработчике (фактически внутри метода расширения ToOkOrProblemDetail
), после возврата к промежуточному программному обеспечению он меняется на 204. Это неожиданное изменение приводит к ошибкам при попытке записи в тело ответа: «Запись в тело ответа недопустима для ответов с кодом состояния 204».
Я был бы признателен за любые идеи или предложения о том, почему код состояния меняется с 200 на 204 после конвейера промежуточного программного обеспечения и как предотвратить это. Может ли использование библиотеки FastEndpoint
повлиять на такое поведение?
Вот что я пробовал до сих пор:
Копирование ответа: для копирования ответа использовались разные методы, включая context.Response.WriteAsync
и context.Response.CopyAsync
.
Явное задание длины контента: попробовал явно установить Content-Length
в обработчике.
Размещение промежуточного программного обеспечения: промежуточное программное обеспечение перемещено в разные места в конвейере, чтобы увидеть, влияет ли размещение на поведение.
Ручная установка кода состояния: Пробовал как устанавливать, так и не устанавливать код состояния вручную в промежуточном программном обеспечении.
Несмотря на эти попытки, код статуса по-прежнему меняется с 200 на 204 после _next(context)
.
@YumiaoKong Спасибо за ваш отзыв. Я хотел сообщить вам, что проблема решена. Проблема действительно была связана с FastEndpoints. Используя метод HttpContext.MarkResponseStart()
в своем методе расширения ToOkOrProblemDetail()
, я смог сообщить FastEndpoints, что обрабатываю ответ, что решило проблему.
при написании ответа в поток ответов самостоятельно, как вы это делаете в методе расширения ToOkOrProblemDetail()
, вам необходимо сообщить fastdpoints, что вы позаботитесь об отправке ответа следующим образом:
HttpContext.MarkResponseStart()
это метод расширения, предоставляемый FE. это показано в документации здесь относительно написания собственных Send*Async()
методов в качестве расширений.
Я считаю, что это должно решить проблему. если нет, создайте новую задачу либо в репозитории FE на GitHub, либо на сервере Discord с проектом воспроизведения с вашим кодом.
Большое спасибо за ваш ответ! Добавление HttpContext.MarkResponseStart()
в мой метод расширения ToOkOrProblemDetail()
сработало отлично. Ваше руководство о том, как сообщить FastEndpoints о том, что я обрабатываю ответ, было именно тем, что мне нужно.
Кода, связанного с _mediator и CreateAuthorizationCommand, нет, поэтому я не могу воспроизвести вашу проблему. Не могли бы вы предоставить более подробный код? Пробовали ли вы проверить, сохраняется ли проблема, без использования библиотеки FastEndpoint?