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

ASP.NET Core MVC实现累计访客/访问量统计的最优方案

Optimal Way to Track Total Visitors in ASP.NET Core MVC

Great question! Since ASP.NET Core doesn't have Global.asax or the Session_Start event we relied on in traditional MVC, we need to use modern pipeline-based approaches. Let's walk through the best way to implement this, including how to reliably identify new visitors.

First: Configure Session Support

We'll use Session (or persistent cookies) to mark whether a visitor has already been counted, so we don't increment the count on every page load. Start by setting up Session in your Program.cs:

var builder = WebApplication.CreateBuilder(args);

// Add Session with a reasonable timeout (adjust based on your needs)
builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromHours(24); // Reset if user is inactive for 24h
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true; // Ensures cookie works even if user declines non-essential cookies (for compliance)
});

builder.Services.AddControllersWithViews();
builder.Services.AddDbContext<YourDbContext>(options => 
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

var app = builder.Build();

// Enable Session BEFORE routing and endpoints
app.UseSession();

// Add our visitor counter middleware (we'll build this next)
app.UseVisitorCounter();

app.UseRouting();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Middleware is the cleanest way to handle global logic like visitor counting—it runs on every request, so you don't have to repeat code in controllers.

Create a VisitorCounterMiddleware class:

public class VisitorCounterMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IServiceScopeFactory _scopeFactory;

    public VisitorCounterMiddleware(RequestDelegate next, IServiceScopeFactory scopeFactory)
    {
        _next = next;
        _scopeFactory = scopeFactory;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Check if this visitor has already been counted via Session
        if (!context.Session.GetBoolean("IsVisitorCounted"))
        {
            // Create a scope to access our DbContext (since middleware is singleton)
            using var scope = _scopeFactory.CreateScope();
            var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>();

            // Get or create the total visitors record (adjust your entity model as needed)
            var visitorSetting = await dbContext.Settings
                .FirstOrDefaultAsync(s => s.Key == "TotalVisitors");

            if (visitorSetting == null)
            {
                // Initialize if no record exists
                dbContext.Settings.Add(new Setting 
                { 
                    Key = "TotalVisitors", 
                    TotalValue = 1 // Use an int field instead of string for easier increments!
                });
            }
            else
            {
                // Atomically increment the count to avoid concurrency issues
                visitorSetting.TotalValue += 1;
            }

            await dbContext.SaveChangesAsync();

            // Mark this visitor as counted in their session
            context.Session.SetBoolean("IsVisitorCounted", true);
        }

        // Pass the request to the next middleware in the pipeline
        await _next(context);
    }
}

// Extension method to easily register the middleware
public static class VisitorCounterMiddlewareExtensions
{
    public static IApplicationBuilder UseVisitorCounter(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<VisitorCounterMiddleware>();
    }
}

2. How to Reliably Track "New" Visitors

The above uses Session (cookie-based) to track visitors, which works for most cases. Here are ways to refine this:

  • Persistent Cookies for Longer Tracking: If you want to avoid counting the same user again within a set period (e.g., 30 days), replace Session with a persistent cookie:
    // Replace the Session check with this
    if (!context.Request.Cookies.ContainsKey("VisitorCounted"))
    {
        // Count logic here...
    
        // Set a cookie that expires in 30 days
        context.Response.Cookies.Append("VisitorCounted", "true", new CookieOptions
        {
            Expires = DateTimeOffset.Now.AddDays(30),
            HttpOnly = true,
            IsEssential = true
        });
    }
    
  • Combine with User Authentication: If your site has logged-in users, use their UserId to avoid counting them multiple times across sessions. For anonymous users, fall back to cookies/Session.
  • IP + UserAgent (Less Reliable): You could combine the user's IP address and browser UserAgent to identify unique visitors, but this can miscount (e.g., shared office IPs, users switching browsers). Use this only as a backup.

3. Critical Considerations

  • Concurrency Safety: When multiple visitors hit your site at once, naive updates can cause race conditions. Use EF Core's ExecuteUpdate for atomic increments to fix this:
    // Replace the manual increment with this atomic operation
    await dbContext.Settings
        .Where(s => s.Key == "TotalVisitors")
        .ExecuteUpdateAsync(s => s.SetProperty(p => p.TotalValue, p.TotalValue + 1));
    
  • Performance for High Traffic: If your site gets millions of visitors, hitting the database on every new visitor will slow things down. Use a distributed cache like Redis to increment counts in memory, then sync to the database periodically (e.g., nightly).
  • Compliance: If you're targeting EU users, ensure your cookie usage complies with GDPR—make sure users can opt out if your counting cookie is considered non-essential.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 10:11:07