ASP.NET SignalR 1.0 實現的一個特性HubPipeline -實現任何訊息incoming和outgoing的攔截。SignalR HubPipeline功能對應的ASP.NET Web API和ASP.NET MVC的 ActionFilter。
下面的方法是作為一個實體在IHubPipelineModule定義:
public interface IHubPipelineModule{ Func<IHubIncomingInvokerContext, Task<object>> BuildIncoming(Func<IHubIncomingInvokerContext, Task<object>> invoke); Func<IHubOutgoingInvokerContext, Task> BuildOutgoing(Func<IHubOutgoingInvokerContext, Task> send); Func<IHub, Task> BuildConnect(Func<IHub, Task> connect); Func<IHub, Task> BuildReconnect(Func<IHub, Task> reconnect); Func<IHub, Task> BuildDisconnect(Func<IHub, Task> disconnect); Func<HubDescriptor, IRequest, bool> BuildAuthorizeConnect(Func<HubDescriptor, IRequest, bool> authorizeConnect); Func<HubDescriptor, IRequest, IList<string>, IList<string>> BuildRejoiningGroups(Func<HubDescriptor, IRequest, IList<string>, IList<string>> rejoiningGroups);}
是不是感覺有非常複雜的委託。不用急, HubPipelineModule 類已經為我們實現了大部分的功能,大部分情況下已經夠用了,可以繼承這個雷重寫裡面方法就可以了.
public abstract class HubPipelineModule : IHubPipelineModule{ protected virtual bool OnBeforeAuthorizeConnect(HubDescriptor hubDescriptor, IRequest request); protected virtual bool OnBeforeConnect(IHub hub); protected virtual void OnAfterConnect(IHub hub); protected virtual bool OnBeforeReconnect(IHub hub); protected virtual void OnAfterReconnect(IHub hub); protected virtual bool OnBeforeOutgoing(IHubOutgoingInvokerContext context); protected virtual void OnAfterOutgoing(IHubOutgoingInvokerContext context); protected virtual bool OnBeforeDisconnect(IHub hub); protected virtual void OnAfterDisconnect(IHub hub); protected virtual bool OnBeforeIncoming(IHubIncomingInvokerContext context); protected virtual object OnAfterIncoming(object result, IHubIncomingInvokerContext context); protected virtual void OnIncomingError(Exception ex, IHubIncomingInvokerContext context);}
這代碼看起來是不是和ASP.NET Web API和ASP.NET MVC的 ActionFilter 很相似呢。如果其中稱為OnBefore的方法返回false,它會立即結束。
public class AntiClickModule : HubPipelineModule{ public AntiClickModule() { Interval = 1000; } public int Interval { get; set; } private readonly ConcurrentDictionary<string, DateTime> _connections = new ConcurrentDictionary<string, DateTime>(); protected override void OnAfterDisconnect(IHub hub) { DateTime lastDateTime; _connections.TryRemove(hub.Context.ConnectionId, out lastDateTime); } protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context) { var now = DateTime.Now; var connectionId = context.Hub.Context.ConnectionId; DateTime lastDateTime; if (_connections.TryGetValue(connectionId, out lastDateTime)) { var span = now - lastDateTime; if (span.TotalMilliseconds < Interval) { return false; } } _connections.AddOrUpdate(connectionId, now, (_, __) => now); return true; }}
記錄下串連到每個ID所請求的時間,我們簡單地檢查下一個請求的時間間隔。 如果2個請求之間的時間差小於我們設定的時間間隔,直接返回。
您可以把它添加到使用GlobalHost的模組Global.asax.cs
protected void Application_Start(object sender, EventArgs e){ GlobalHost.HubPipeline.AddModule(new AntiClickModule());}