在使用Blazor Server开发的时候,在一些特定场景下,我们可能需要获取访客的连接数(一个访客打开多个页面产生的连接数)或者访客数量
先看看最终效果
首先我们需要创建一个自定义的CircuitHandler,Circuit表示的是服务器上ASP.NET Core组件与客户端之间的链接,CircuitHandler则是类似拦截器,在建立链接时会触发
//Circuit客户端的基本信息
public class CircuitClientInfo
{
public string CircuitId { get; set; } = "";
public DateTimeOffset JoinDateTime { get; set; } = DateTimeOffset.Now;
}
//使用静态方法记录Circuit连接的客户端,全局的,并且线程安全的
public static class CircuitTrackerGlobalInfo
{
public static ConcurrentDictionary<string, CircuitClientInfo> CircuitClients { get; set; } = new();
public static ConcurrentDictionary<string, string> VisitorClients { get; set; } = new();
}
public class CircuitTracker : CircuitHandler
{
private CircuitClientInfo _circuitInfo = new();
private string _visitorId = "";
public int ConnectionCount => CircuitTrackerGlobalInfo.CircuitClients.Count;
public int VisitorCount => CircuitTrackerGlobalInfo.VisitorClients.GroupBy(x => x.Value).Count();
public string CircuitId => _circuitInfo.CircuitId;
public string VisitorId => _visitorId;
public override Task OnConnectionUpAsync(Circuit circuit, CancellationToken cancellationToken)
{
_circuitInfo.CircuitId = circuit.Id;
_circuitInfo.JoinDateTime = DateTimeOffset.Now;
CircuitTrackerGlobalInfo.CircuitClients.TryAdd(circuit.Id, _circuitInfo);
return Task.CompletedTask;
}
public override Task OnConnectionDownAsync(Circuit circuit, CancellationToken cancellationToken)
{
CircuitTrackerGlobalInfo.CircuitClients.TryRemove(circuit.Id, out _);
CircuitTrackerGlobalInfo.VisitorClients.TryRemove(circuit.Id, out _);
return Task.CompletedTask;
}
public void VisitorRegister(string visitorId)
{
_visitorId = visitorId;
CircuitTrackerGlobalInfo.VisitorClients.TryAdd(CircuitId, visitorId);
}
}
然后在Program.cs以Scoped生命周期注入,给每个连接的客户端都分配一个Circuit跟踪器
builder.Services.AddScoped<CircuitHandler, CircuitTracker>();
然后在App.razor或者index.html的head部分加入以下js代码,用于获取浏览器指纹,判断唯一访客
<script>
function getVisitorId() {
return import('https://openfpcdn.io/fingerprintjs/v4')
.then(FingerprintJS => FingerprintJS.load())
.then(fp => fp.get())
.then(result => {
return result.visitorId;
});
}
</script>
然后在你想要的页面按下面的例子实现就可以啦
@page "/counter"
@using Microsoft.AspNetCore.Components.Server.Circuits
@rendermode InteractiveServer //这个很重要,需要在服务端渲染
@inject CircuitHandler CircuitTracker
@inject IJSRuntime _jSRuntime
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @circuitTracker.ConnectionCount</p>
<p role="status">Visitor count: @circuitTracker.VisitorCount</p>
<p role="status">CircuitId: @circuitTracker.CircuitId</p>
<p role="status">VisitorId: @circuitTracker.VisitorId</p>
@code{
private CircuitTracker circuitTracker;
protected override void OnInitialized()
{
circuitTracker = (CircuitTracker)CircuitTracker;
base.OnInitialized();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var visitorId = await _jSRuntime.InvokeAsync<string>("getVisitorId");
//因为获取访客ID是通过js获取的,所以需要在页面渲染了后调用,并且注册到跟踪器
(CircuitTracker as CircuitTracker)!.VisitorRegister(visitorId);
StateHasChanged();
}
await base.OnAfterRenderAsync(firstRender);
}
}
源码例子可以查看原文,或者访问(https://gitee.com/imxcstar/blazor-server-visitor-demo.git)