在本文中,我们将学习如何使用 Microsoft.AspNetCore.Http.Timeouts 命名空间在 .NET 8 中请求超时中间件工作。此命名空间提供了应用每个请求的超时的灵活性。当达到超时限制时,HttpContext.RequestAborted 中 CancellationToken 的 IsCancellationRequested 属性设置为 true。但是,请求不会自动中止,因此应用程序仍会生成成功或失败响应。如果应用程序未处理异常并生成响应,则默认行为是返回状态代码 504。
作者创建的图像
让我们看看如何为标准 .NET Web API 和最小 API 配置超时中间件。现在让我们看看如何增加Program.cs
using Microsoft.AspNetCore.Http.Timeouts;
using Newtonsoft.Json;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddRequestTimeouts();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.UseRequestTimeouts();
app.MapControllers();
app.Run();
现在,让我们看看如何配置终结点,以便为 Web API 和最小 API 设置 RequestTimeouts。
[HttpGet("twosecondtimeout/{waitSeconds:int}")]
[RequestTimeout(2000)]
public async Task<IActionResult> GetCustomerWithTwoSecondTimeoutAsync([FromRoute] int waitSeconds)
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), HttpContext.RequestAborted);
return Ok();
}
app.MapGet("/twosecondtimeout/{waitSeconds:int}", async ([FromRoute] int waitSeconds, HttpContext context) =>
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), context.RequestAborted);
return Results.Ok();
}).WithRequestTimeout(TimeSpan.FromSeconds(2));
当您超过配置的超时时,您将看到 API 返回以下状态代码。
_注意:_当应用程序在调试模式下运行时,超时中间件不会激活。此行为与 Kestrel 超时一致。若要测试超时,请在不附加调试器的情况下运行应用程序 (ctrl + F5)。
现在,让我们深入探讨一些高级选项,例如命名策略、自定义状态代码和自定义响应。
配置命名策略
我们可以定义命名的 RequestTimeout 策略,并使用它来配置端点,如下所示:
builder.Services.AddRequestTimeouts(options =>
{
options.AddPolicy("threesecondpolicy", TimeSpan.FromSeconds(3));
});
[HttpGet("threesecondtimeout/{waitSeconds:int}")]
[RequestTimeout("threesecondpolicy")]
public async Task<IActionResult> GetCustomerWithThreeSecondTimeoutPolicyAsync([FromRoute] int waitSeconds)
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), HttpContext.RequestAborted);
return Ok();
}
app.MapGet("/threesecondtimeout/{waitSeconds:int}", async ([FromRoute] int waitSeconds, HttpContext context) =>
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), context.RequestAborted);
return Results.Ok();
}).WithRequestTimeout("threesecondpolicy");
配置自定义状态代码
在定义策略时,我们可以定义要返回的自定义状态代码,如下所示:
builder.Services.AddRequestTimeouts(options =>
{
options.AddPolicy("customstatuscode", new RequestTimeoutPolicy
{
Timeout = TimeSpan.FromSeconds(3),
TimeoutStatusCode = 503
});
});
[HttpGet("customstatuscode/{waitSeconds:int}")]
[RequestTimeout("customstatuscode")]
public async Task<IActionResult> GetCustomerWithCustomStatusCodePolicyAsync([FromRoute] int waitSeconds)
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), HttpContext.RequestAborted);
return Ok();
}
app.MapGet("/customstatuscode/{waitSeconds:int}", [RequestTimeout("customstatuscode")] async ([FromRoute] int waitSeconds, HttpContext context) =>
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), context.RequestAborted);
return Results.Ok();
});
当您的请求超时超过配置的超时时,我们可以看到 API 返回配置的状态代码。
配置自定义委托以自定义响应
在定义策略时,我们可以定义自定义委托来自定义返回响应,如下所示:
builder.Services.AddRequestTimeouts(options =>
{
options.AddPolicy("customdelegatepolicy", new RequestTimeoutPolicy
{
Timeout = TimeSpan.FromSeconds(3),
TimeoutStatusCode = 503,
WriteTimeoutResponse = async (HttpContext context) => {
context.Response.ContentType = "application/json";
var errorResponse = new
{
error = "Request time out from custome delegate policy",
status = 503
};
var jsonResponse = JsonConvert.SerializeObject(errorResponse);
await context.Response.WriteAsync(jsonResponse);
}
});
});
[HttpGet("customdelegatepolicy/{waitSeconds:int}")]
[RequestTimeout("customdelegatepolicy")]
public async Task<IActionResult> GetCustomerWithCustomDelegateAsync([FromRoute] int waitSeconds)
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), HttpContext.RequestAborted);
return Ok();
}
app.MapGet("/customdelegatepolicy/{waitSeconds:int}", async ([FromRoute] int waitSeconds, HttpContext context) =>
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), context.RequestAborted);
return Results.Ok();
}).WithRequestTimeout("customdelegatepolicy");
当您的请求超时超过配置的超时时,我们可以看到 API 返回以下响应。
全局默认请求超时策略
就像我们在每个端点定义策略和配置策略的方式一样,我们可以将全局超时策略定义为捕获所有策略,如下所示:
builder.Services.AddRequestTimeouts(options =>
{
options.DefaultPolicy = new RequestTimeoutPolicy
{
Timeout = TimeSpan.FromMilliseconds(1500)
};
options.AddPolicy("threesecondpolicy", TimeSpan.FromSeconds(3));
options.AddPolicy("customstatuscode", new RequestTimeoutPolicy
{
Timeout = TimeSpan.FromSeconds(3),
TimeoutStatusCode = 503
});
options.AddPolicy("customdelegatepolicy", new RequestTimeoutPolicy
{
Timeout = TimeSpan.FromSeconds(3),
TimeoutStatusCode = 503,
WriteTimeoutResponse = async (HttpContext context) => {
context.Response.ContentType = "application/json";
var errorResponse = new
{
error = "Request time out from custome delegate policy",
status = 503
};
var jsonResponse = JsonConvert.SerializeObject(errorResponse);
await context.Response.WriteAsync(jsonResponse);
}
});
});
禁用请求超时
配置全局默认超时策略后,所有终结点都将使用该策略强制执行,但在某些情况下,我们可能需要排除某些终结点应用该策略。我们可以使用 DisableRequestTimeout 属性来实现这一点,如下所示:
[HttpGet("disableTimeout/{waitSeconds:int}")]
[DisableRequestTimeout]
public async Task<IActionResult> GetCustomerWithNoTimeoutAsync([FromRoute] int waitSeconds)
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), HttpContext.RequestAborted);
return Ok();
}
app.MapGet("/disablerequestTimeout/{waitSeconds:int}", async ([FromRoute] int waitSeconds, HttpContext context) =>
{
await Task.Delay(TimeSpan.FromSeconds(waitSeconds), context.RequestAborted);
return Results.Ok();
}).DisableRequestTimeout();
我们在这篇博客中介绍了有关请求超时中间件的所有内容。我们将在以后的博客中介绍有关 .NET Web API 的更多主题。
源代码获取:公众号回复消息【code:46036
】