ASP.NET Core Web API第三方API响应超时处理方案咨询
ASP.NET Core Web API 超时返回+后台处理实现方案
核心逻辑
通过Task.WhenAny同时等待第三方API请求和超时延迟任务:
- 若第三方请求先完成,直接返回结果给用户;
- 若超时延迟先触发,立即返回错误响应,同时启动后台任务继续等待第三方响应,完成后将结果存储。
代码实现
1. 自定义响应存储服务
先定义一个用于存储第三方响应的服务,这里以内存存储为例(实际可替换为数据库、Redis等持久化存储):
public interface IResponseStorageService { Task StoreResponseAsync(string requestId, string responseContent); } public class InMemoryResponseStorageService : IResponseStorageService { private readonly Dictionary<string, string> _storage = new(); private readonly object _lockObj = new(); public Task StoreResponseAsync(string requestId, string responseContent) { lock (_lockObj) { _storage[requestId] = responseContent; } return Task.CompletedTask; } }
2. Controller 实现
在ControlDevice接口中实现超时控制和后台处理逻辑,注入IHttpClientFactory和IResponseStorageService:
[ApiController] [Route("api/[controller]")] public class ControlDeviceController : ControllerBase { private readonly HttpClient _httpClient; private readonly IResponseStorageService _storageService; private readonly ILogger<ControlDeviceController> _logger; private const int TimeoutSeconds = 3; // 设置3秒超时 public ControlDeviceController(IHttpClientFactory httpClientFactory, IResponseStorageService storageService, ILogger<ControlDeviceController> logger) { _httpClient = httpClientFactory.CreateClient("ThirdPartyApi"); _storageService = storageService; _logger = logger; } [HttpPost] public async Task<IActionResult> ControlDevice([FromBody] DeviceControlRequest request) { var requestId = Guid.NewGuid().ToString(); // 生成唯一请求ID,用于关联后续响应 var thirdPartyTask = CallThirdPartyApiAsync(request, requestId); // 等待第三方请求或超时 var completedTask = await Task.WhenAny(thirdPartyTask, Task.Delay(TimeSpan.FromSeconds(TimeoutSeconds))); if (completedTask == thirdPartyTask) { // 第三方请求在超时前完成,直接返回结果 var response = await thirdPartyTask; return Ok(response); } else { // 超时,立即返回错误,后台继续处理 _ = Task.Run(async () => { try { var delayedResponse = await thirdPartyTask; await _storageService.StoreResponseAsync(requestId, delayedResponse); _logger.LogInformation("第三方延迟响应已存储,RequestId: {RequestId}", requestId); } catch (Exception ex) { _logger.LogError(ex, "后台处理第三方响应失败,RequestId: {RequestId}", requestId); } }); return StatusCode(StatusCodes.Status504GatewayTimeout, new { Message = "请求超时,后台将继续处理设备控制任务", RequestId = requestId }); } } private async Task<string> CallThirdPartyApiAsync(DeviceControlRequest request, string requestId) { // 构造第三方API请求,这里示例用PostAsync,实际根据第三方接口调整 var content = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync("/api/device/control", content); response.EnsureSuccessStatusCode(); // 若第三方返回错误,抛出异常 return await response.Content.ReadAsStringAsync(); } } // 设备控制请求模型 public class DeviceControlRequest { public string DeviceId { get; set; } public string Command { get; set; } }
3. 服务注册
在Program.cs中注册所需服务:
var builder = WebApplication.CreateBuilder(args); // 添加HttpClient,配置第三方API基础地址 builder.Services.AddHttpClient("ThirdPartyApi", client => { client.BaseAddress = new Uri("https://third-party-api.example.com/"); }); // 注册响应存储服务 builder.Services.AddSingleton<IResponseStorageService, InMemoryResponseStorageService>(); builder.Services.AddControllers(); // ... 其他服务注册 var app = builder.Build(); // ... 中间件配置 app.Run();
关键注意事项
- 后台任务可靠性:上述示例用
Task.Run启动后台任务,若应用进程重启,未完成的任务会丢失。如果需要更高可靠性,建议使用Hangfire、Quartz等任务调度框架,或者将任务提交到消息队列(如RabbitMQ),由独立的后台服务消费处理。 - 第三方请求取消:示例中未给第三方请求传递取消令牌,确保超时后请求仍能继续执行。若需要根据业务场景取消请求,可调整
CallThirdPartyApiAsync方法传入CancellationToken,但要注意这会终止第三方请求,无法获取后续响应。 - 异常处理:后台任务必须捕获所有异常,避免未处理异常导致进程崩溃,同时通过日志记录错误信息便于排查。
内容的提问来源于stack exchange,提问作者Harsh Joshi
相关产品推荐
相关产品推荐

