.NET Core 6 API如何取消超时请求并返回504状态码?
在.NET Core 6 API中实现AD查询超时取消并返回504状态码
因为IIS进程内托管模式下,web.config的requestTimeout和IIS原生超时设置不会生效,必须通过.NET Core自身的请求管道和任务取消机制来实现需求,以下是具体实现方案:
一、针对单个接口的超时控制(推荐)
1. 修改服务层方法,支持CancellationToken
确保服务层的AD查询异步方法接受CancellationToken参数,让超时信号能传递到AD查询逻辑中:
public async Task<AdUser> GetAdUserAsync(string userId, CancellationToken cancellationToken) { using var context = new PrincipalContext(ContextType.Domain); // 将CancellationToken传入支持异步取消的AD方法(如果方法支持的话) var user = await UserPrincipal.FindByIdentityAsync(context, userId); // 若AD方法不直接支持CancellationToken,用Task.WhenAny包装实现超时 // var adTask = UserPrincipal.FindByIdentityAsync(context, userId); // var timeoutTask = Task.Delay(Timeout.Infinite, cancellationToken); // var completedTask = await Task.WhenAny(adTask, timeoutTask); // if (completedTask == timeoutTask) throw new OperationCanceledException("AD查询超时"); // var user = adTask.Result; if (user == null) throw new KeyNotFoundException("目标AD用户不存在"); return new AdUser { Id = user.SamAccountName, Name = user.DisplayName }; }
2. 控制器中设置超时并处理取消逻辑
在API接口里创建带指定超时时间的CancellationTokenSource,调用服务方法时传入令牌,捕获超时异常后返回504状态码:
[ApiController] [Route("api/ad")] public class AdController : ControllerBase { private readonly IAdService _adService; private readonly ILogger<AdController> _logger; public AdController(IAdService adService, ILogger<AdController> logger) { _adService = adService; _logger = logger; } [HttpGet("user/{userId}")] public async Task<IActionResult> GetAdUser(string userId) { // 设置超时时间为x分钟,这里示例为5分钟 using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5)); try { var adUser = await _adService.GetAdUserAsync(userId, cts.Token); return Ok(adUser); } catch (OperationCanceledException) { _logger.LogWarning("AD查询超时,用户ID:{UserId}", userId); return StatusCode(StatusCodes.Status504GatewayTimeout, "AD查询超时,请稍后重试"); } catch (Exception ex) { _logger.LogError(ex, "AD查询失败,用户ID:{UserId}", userId); return StatusCode(StatusCodes.Status500InternalServerError, "查询出错"); } } }
二、全局请求超时配置(可选)
如果需要给所有API接口统一设置超时规则,可以通过自定义中间件实现:
1. 编写超时中间件
public class RequestTimeoutMiddleware { private readonly RequestDelegate _next; private readonly TimeSpan _globalTimeout; public RequestTimeoutMiddleware(RequestDelegate next, IConfiguration config) { _next = next; // 从appsettings.json读取全局超时时间(示例:"RequestTimeoutMinutes": 5) _globalTimeout = TimeSpan.FromMinutes(config.GetValue<int>("RequestTimeoutMinutes")); } public async Task InvokeAsync(HttpContext context) { using var cts = CancellationTokenSource.CreateLinkedTokenSource(context.RequestAborted); cts.CancelAfter(_globalTimeout); context.RequestAborted = cts.Token; try { await _next(context); } catch (OperationCanceledException) when (context.RequestAborted.IsCancellationRequested) { if (!context.Response.HasStarted) { context.Response.StatusCode = StatusCodes.Status504GatewayTimeout; await context.Response.WriteAsync("请求超时,请稍后重试"); } } } } // 扩展方法用于注册中间件 public static class RequestTimeoutMiddlewareExtensions { public static IApplicationBuilder UseGlobalRequestTimeout(this IApplicationBuilder builder) { return builder.UseMiddleware<RequestTimeoutMiddleware>(); } }
2. 在Program.cs中注册中间件
var builder = WebApplication.CreateBuilder(args); // 注册服务(如IAdService、日志等) builder.Services.AddScoped<IAdService, AdService>(); builder.Services.AddControllers(); var app = builder.Build(); // 在路由中间件之后、授权中间件之前注册超时中间件 app.UseRouting(); app.UseGlobalRequestTimeout(); app.UseAuthorization(); app.MapControllers(); app.Run();
关键说明
- 进程内托管模式下,ASP.NET Core完全接管请求处理,IIS的超时设置不会作用于.NET Core的请求管道,必须通过代码层面的
CancellationToken实现超时控制。 - 如果AD的异步方法不直接支持
CancellationToken,一定要用Task.WhenAny将查询任务与超时任务绑定,确保超时后能终止等待并抛出取消异常。
内容的提问来源于stack exchange,提问作者dev
相关产品推荐
相关产品推荐

