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

AspNet Core如何实现接口请求次数统计以获取访问量与热门分类数据

实现方案

默认以ASP.NET Core环境为例(你提到的Controller/Endpoint是.NET栈常用概念,其他技术栈逻辑通用,可对应调整拦截组件)

第一步:全局请求拦截(无侵入式统计)

推荐用中间件或Action过滤器实现全局拦截,不需要在每个Endpoint单独写统计代码。

1. 定义统计服务

先做计数存储的封装,示例用内存存储,需要持久化可自行替换为数据库/Redis:

public interface IApiStatsService
{
    // 统计接口总调用次数
    void IncrementEndpointCall(string fullEndpointPath);
    // 统计单个分类的访问次数
    void IncrementCategoryVisit(int categoryId);
    // 获取调用次数最高的接口
    (string MostVisitedEndpoint, int CallCount) GetMostVisitedEndpoint();
    // 获取访问量最高的分类ID
    int GetMostPopularCategoryId();
}

public class InMemoryApiStatsService : IApiStatsService
{
    // 线程安全的字典,避免并发计数出错
    private readonly ConcurrentDictionary<string, int> _endpointCallStats = new();
    private readonly ConcurrentDictionary<int, int> _categoryVisitStats = new();

    public void IncrementEndpointCall(string fullEndpointPath)
    {
        _endpointCallStats.AddOrUpdate(fullEndpointPath, 1, (_, oldValue) => oldValue + 1);
    }

    public void IncrementCategoryVisit(int categoryId)
    {
        _categoryVisitStats.AddOrUpdate(categoryId, 1, (_, oldValue) => oldValue + 1);
    }

    public (string MostVisitedEndpoint, int CallCount) GetMostVisitedEndpoint()
    {
        if (!_endpointCallStats.Any()) return (string.Empty, 0);
        var maxItem = _endpointCallStats.MaxBy(kv => kv.Value);
        return (maxItem.Key, maxItem.Value);
    }

    public int GetMostPopularCategoryId()
    {
        if (!_categoryVisitStats.Any()) return 0;
        return _categoryVisitStats.MaxBy(kv => kv.Value).Key;
    }
}

在Program.cs注册为单例:

builder.Services.AddSingleton<IApiStatsService, InMemoryApiStatsService>();

2. 实现统计中间件

public class ApiStatsMiddleware
{
    private readonly RequestDelegate _next;

    public ApiStatsMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context, IApiStatsService statsService)
    {
        // 先执行接口业务逻辑
        await _next(context);

        // 仅统计成功响应的请求,不需要可以去掉这个判断
        if (context.Response.StatusCode < 400)
        {
            var routeEndpoint = context.GetEndpoint() as RouteEndpoint;
            if (routeEndpoint == null) return;

            // 取路由模板(比如/category/{id})而不是实际请求路径,避免同接口不同参数生成多条统计
            var routePattern = routeEndpoint.RoutePattern.RawText;
            // 拼接请求方法,区分同路径不同请求方式的接口
            var fullEndpointPath = $"{context.Request.Method.ToUpper()} {routePattern}";
            
            if (!string.IsNullOrEmpty(routePattern))
            {
                statsService.IncrementEndpointCall(fullEndpointPath);

                // 如果是分类详情接口,额外统计分类ID的访问量
                if (fullEndpointPath.Equals("GET /category/{id}", StringComparison.OrdinalIgnoreCase))
                {
                    if (context.Request.RouteValues.TryGetValue("id", out var idObj) 
                        && int.TryParse(idObj?.ToString(), out var categoryId))
                    {
                        statsService.IncrementCategoryVisit(categoryId);
                    }
                }
            }
        }
    }
}

在Program.cs注册中间件,放在路由中间件之后、端点执行之前:

app.UseRouting();
// 统计中间件加在这里
app.UseMiddleware<ApiStatsMiddleware>();
app.UseAuthorization();
app.MapControllers();

第二步:获取统计结果

可以单独做一个统计接口返回你需要的结果:

[ApiController]
[Route("api/stats")]
public class StatsController : ControllerBase
{
    private readonly IApiStatsService _statsService;

    public StatsController(IApiStatsService statsService)
    {
        _statsService = statsService;
    }

    [HttpGet("most-popular-category")]
    public IActionResult GetMostPopularCategory()
    {
        var mostPopularId = _statsService.GetMostPopularCategoryId();
        if (mostPopularId == 0) return Ok("No category has been visited yet");
        return Ok($"{mostPopularId} is the most popular category");
    }

    [HttpGet("endpoint-call-rank")]
    public IActionResult GetEndpointCallRank()
    {
        var (endpoint, count) = _statsService.GetMostVisitedEndpoint();
        return Ok(new { MostVisitedEndpoint = endpoint, CallCount = count });
    }
}

其他技术栈适配逻辑

核心逻辑都是「拦截请求 -> 计数 -> 读统计结果」,替换对应拦截组件即可:

  • Java SpringBoot:用HandlerInterceptor或者AOP实现拦截
  • Node.js Express/Koa:用自定义中间件实现拦截

注意事项

  • 示例用内存存储,服务重启后数据会丢失,需要持久化或高并发场景建议换Redis,用INCR指令做原子计数,性能更好
  • 如果不需要统计所有接口,可以给要统计的接口加自定义特性,拦截器只统计带特性标记的接口即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 22:27:04