惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
博客园_首页
罗磊的独立博客
云风的 BLOG
云风的 BLOG
J
Java Code Geeks
Last Week in AI
Last Week in AI
D
DataBreaches.Net
Jina AI
Jina AI
博客园 - Franky
大猫的无限游戏
大猫的无限游戏
Apple Machine Learning Research
Apple Machine Learning Research
V
V2EX
D
Docker
MongoDB | Blog
MongoDB | Blog
B
Blog RSS Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
宝玉的分享
宝玉的分享
Engineering at Meta
Engineering at Meta
The Cloudflare Blog
博客园 - 三生石上(FineUI控件)
有赞技术团队
有赞技术团队
人人都是产品经理
人人都是产品经理
H
Help Net Security
T
The Blog of Author Tim Ferriss

博客园 - 火冰·瓶

Cloudflare Tunnel 有两种模式 用 Cloudflare Tunnel 实现从外网 SSH 登录内网服务器 Cloudflare + .NET Core MVC 部署完整流程 net core 后端获取网络图片的扩展名 推荐好用的web控件 ubuntu运维 使用Frp+Caddy把https映射到内网的web服务 net core中结合EntityFrameworkCore的DB first使用PostgreSQL OneDrive中设置不同步某个文件夹 Framework 4.7老项目运行到caddy Action<T> 和 Func<T>的用法 EF Core经验 net core随记 asp.net core 记录页面访问次数,同一个IP一个小时只算一次 asp.net core 发布到caddy asp.net core发布到Caddy获取用户的真实ip 统计文章阅读量,一小时内重复刷新的不重复统计 使用on()方法绑定事件,解决动态加载的元素事件绑定 鼠标悬停在图片上方时,显示文字
asp.net core 记录页面访问记录
火冰·瓶 · 2025-04-02 · via 博客园 - 火冰·瓶

方案 1:使用中间件

中间件适用于所有请求,它可以在请求进入 Controller 之前执行统计操作。

1. 创建 VisitMiddleware

public class VisitMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly TimeSpan _timeSpan = TimeSpan.FromHours(1);

    public VisitMiddleware(RequestDelegate next, IServiceScopeFactory scopeFactory)
    {
        _next = next;
        _scopeFactory = scopeFactory; // 用于创建独立的 `DbContext`
    }

    public async Task InvokeAsync(HttpContext context)
    {
        using (var scope = _scopeFactory.CreateScope())
        {
            var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
            string ipAddress = context.Connection.RemoteIpAddress?.ToString() ?? "Unknown";
            string pageUrl = context.Request.Path;

            var oneHourAgo = DateTime.Now - _timeSpan;
            var existingVisit = dbContext.PageVisits
                .Where(v => v.IpAddress == ipAddress && v.PageUrl == pageUrl && v.VisitTime >= oneHourAgo)
                .FirstOrDefault();

            if (existingVisit == null)
            {
                dbContext.PageVisits.Add(new PageVisit
                {
                    IpAddress = ipAddress,
                    PageUrl = pageUrl,
                    VisitTime = DateTime.Now
                });
                await dbContext.SaveChangesAsync();
            }
        }

        await _next(context); // 继续执行后续请求
    }
}

2. 注册中间件

Program.cs 添加:

app.UseMiddleware<VisitMiddleware>();

方案 2:使用 ActionFilter

ActionFilter 适用于 Controller 层,它比中间件更灵活,可以指定仅在某些 ControllerAction 上执行。

1. 创建 VisitActionFilter

using Microsoft.AspNetCore.Mvc.Filters;

public class VisitActionFilter : IActionFilter
{
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly TimeSpan _timeSpan = TimeSpan.FromHours(1);

    public VisitActionFilter(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        using (var scope = _scopeFactory.CreateScope())
        {
            var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
            string ipAddress = context.HttpContext.Connection.RemoteIpAddress?.ToString() ?? "Unknown";
            string pageUrl = context.HttpContext.Request.Path;

            var oneHourAgo = DateTime.UtcNow - _timeSpan;
            var existingVisit = dbContext.PageVisits
                .Where(v => v.IpAddress == ipAddress && v.PageUrl == pageUrl && v.VisitTime >= oneHourAgo)
                .FirstOrDefault();

            if (existingVisit == null)
            {
                dbContext.PageVisits.Add(new PageVisit
                {
                    IpAddress = ipAddress,
                    PageUrl = pageUrl,
                    VisitTime = DateTime.UtcNow
                });
                dbContext.SaveChanges();
            }
        }
    }

    public void OnActionExecuted(ActionExecutedContext context) { }
}

2. 注册 ActionFilter

Program.cs 中:

services.AddScoped<VisitActionFilter>();

然后,在 Controller 中使用:

[ServiceFilter(typeof(VisitActionFilter))]
public class MyController : ControllerBase
{
    // 所有请求自动记录访问
}

选哪种方案?

  • 中间件 (Middleware) 适用于 所有请求,不会局限于某个 Controller

  • 过滤器 (ActionFilter) 适用于 特定的 ControllerAction,如果你不想记录所有页面,可以选择这个方案。