You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core实现外部Web请求X分钟缓存的方案咨询

缓存外部API请求的几种实用方案

我来分享几个能帮你实现外部API请求缓存的方案,兼顾速度优化,包括你提到的DelegatingHandler实现,还有其他更灵活的方式:

方案一:自定义DelegatingHandler实现缓存

这是你正在尝试的思路,我给你完善一下具体实现,支持内存缓存或者分布式缓存:

首先创建一个缓存Handler:

public class CacheHandler : DelegatingHandler
{
    private readonly IMemoryCache _cache;
    private readonly int _cacheMinutes;

    public CacheHandler(IMemoryCache cache, int cacheMinutes)
    {
        _cache = cache;
        _cacheMinutes = cacheMinutes;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // 用请求的方法和URL作为缓存键,确保唯一性
        var cacheKey = $"{request.Method}_{request.RequestUri}";

        // 先尝试从缓存获取响应
        if (_cache.TryGetValue(cacheKey, out HttpResponseMessage cachedResponse))
        {
            // 返回缓存的响应副本,避免原响应被释放
            return new HttpResponseMessage(cachedResponse.StatusCode)
            {
                Content = new StringContent(await cachedResponse.Content.ReadAsStringAsync()),
                Headers = cachedResponse.Headers
            };
        }

        // 没有缓存就调用外部API
        var response = await base.SendAsync(request, cancellationToken);

        // 如果响应成功,缓存起来
        if (response.IsSuccessStatusCode)
        {
            // 缓存响应的副本,因为原响应会被HttpClient处理释放
            var responseToCache = new HttpResponseMessage(response.StatusCode)
            {
                Content = new StringContent(await response.Content.ReadAsStringAsync()),
                Headers = response.Headers
            };
            _cache.Set(cacheKey, responseToCache, TimeSpan.FromMinutes(_cacheMinutes));
        }

        return response;
    }
}

然后在Startup/Program.cs里注册这个Handler:

builder.Services.AddHttpClient("ExternalApiClient")
    .AddHttpMessageHandler(sp => new CacheHandler(sp.GetRequiredService<IMemoryCache>(), 10)); // 缓存10分钟

这样所有通过这个HttpClient发送的请求都会自动走缓存逻辑,适合全局统一缓存的场景。如果是分布式部署,把IMemoryCache换成IDistributedCache即可。

方案二:使用Polly缓存策略(推荐)

Polly是.NET生态里非常流行的弹性库,缓存是它的核心功能之一,结合HttpClientFactory使用会更简洁,还能搭配重试、熔断等其他策略:

首先安装Polly和Polly.Extensions.Http包,然后注册服务:

builder.Services.AddHttpClient("ExternalApiClient")
    .AddPolicyHandler(Policy.CacheAsync<HttpResponseMessage>(
        new MemoryCacheProvider(new MemoryCache(new MemoryCacheOptions())),
        TimeSpan.FromMinutes(10)));

如果需要更细粒度的控制(比如只缓存特定状态码的响应),可以调整策略:

var cachePolicy = Policy.CacheAsync<HttpResponseMessage>(
    new MemoryCacheProvider(new MemoryCache(new MemoryCacheOptions())),
    TimeSpan.FromMinutes(10),
    onCacheGetAsync: (context, key) =>
    {
        // 缓存命中时的日志逻辑
        return Task.CompletedTask;
    },
    onCachePutAsync: (context, key) =>
    {
        // 缓存写入时的日志逻辑
        return Task.CompletedTask;
    },
    shouldCacheResultAsync: (response, context) =>
    {
        // 只缓存成功的响应
        return Task.FromResult(response.IsSuccessStatusCode);
    });

builder.Services.AddHttpClient("ExternalApiClient")
    .AddPolicyHandler(cachePolicy);

这个方案的好处是不用自己写Handler,Polly已经帮你处理了缓存的细节,还能轻松扩展其他弹性策略。

方案三:在Action层面直接缓存外部API数据

如果只需要在特定Action里缓存某个字段的外部API数据,也可以直接在Action里处理,更灵活:

[ApiController]
[Route("api/[controller]")]
public class MyController : ControllerBase
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IMemoryCache _cache;

    public MyController(IHttpClientFactory httpClientFactory, IMemoryCache cache)
    {
        _httpClientFactory = httpClientFactory;
        _cache = cache;
    }

    [HttpGet]
    public async Task<IActionResult> GetModel()
    {
        var cacheKey = "ExternalApi_Data_FieldX"; // 可以根据请求参数动态生成缓存键
        if (!_cache.TryGetValue(cacheKey, out string externalData))
        {
            var client = _httpClientFactory.CreateClient("ExternalApiClient");
            var response = await client.GetAsync("/external-data");
            response.EnsureSuccessStatusCode();
            externalData = await response.Content.ReadAsStringAsync();
            _cache.Set(cacheKey, externalData, TimeSpan.FromMinutes(10));
        }

        // 构建模型
        var model = new MyModel
        {
            ExternalField = externalData,
            // 其他字段...
        };

        return Ok(model);
    }
}

这种方式适合你只需要缓存特定请求的场景,缓存键可以根据请求参数动态生成,避免不同请求的缓存冲突。

注意事项

  • 如果是分布式系统,一定要用IDistributedCache(比如Redis)代替IMemoryCache,避免多个节点缓存不一致。
  • 缓存键要确保唯一性,最好包含请求的方法、URL和关键参数。
  • 可以根据外部API的缓存策略调整缓存时间,比如遵循外部API返回的Cache-Control头。

内容的提问来源于stack exchange,提问作者zazka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:40:27