Freigeben über


Verwenden von Hubfiltern in ASP.NET Core SignalR

Hubfilter:

  • Sind ab ASP.NET Core 5.0 verfügbar
  • Lassen die Ausführung von Logik vor und nach Aufrufen von Hubmethoden durch Clients zu

Dieser Artikel enthält Anleitungen zum Schreiben und Verwenden von Hubfiltern.

Konfigurieren von Hubfiltern

Hubfilter können global oder basierend auf dem Hubtyp angewendet werden. Die Filter werden in der Reihenfolge hinzugefügt, in der sie ausgeführt werden. Globale werden vor lokalen Hubfiltern ausgeführt.

public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR(options =>
    {
        // Global filters will run first
        options.AddFilter<CustomFilter>();
    }).AddHubOptions<ChatHub>(options =>
    {
        // Local filters will run second
        options.AddFilter<CustomFilter2>();
    });
}

Ein Hubfilter kann auf eine der folgenden Weisen hinzugefügt werden:

  • Hinzufügen eines Filters nach konkretem Typ:

    hubOptions.AddFilter<TFilter>();
    

    Die Auflösung erfolgt über Dependency Injection (DI) oder aktivierten Typ.

  • Hinzufügen eines Filters nach Laufzeittyp:

    hubOptions.AddFilter(typeof(TFilter));
    

    Die Auflösung erfolgt über DI oder aktivierten Typ.

  • Hinzufügen eines Filters nach Instanz:

    hubOptions.AddFilter(new MyFilter());
    

    Diese Instanz wird wie ein Singleton verwendet. Alle Hubmethodenaufrufe verwenden dieselbe Instanz.

Hubfilter werden pro Hubaufruf erstellt und verworfen. Wenn Sie den globalen Zustand im Filter oder keinen Zustand speichern möchten, fügen Sie den Hubfiltertyp zu DI als Singleton hinzu, um die Leistung zu verbessern. Alternativ können Sie den Filter, sofern möglich, als Instanz hinzufügen.

Erstellen von Hubfiltern

Sie erstellen einen Filter, indem Sie eine Klasse deklarieren, die von IHubFilter erbt, und die InvokeMethodAsync-Methode hinzufügen. Es gibt auch OnConnectedAsync und OnDisconnectedAsync, die optional implementiert werden können, um die Hubmethoden OnConnectedAsync bzw. OnDisconnectedAsync zu umschließen.

public class CustomFilter : IHubFilter
{
    public async ValueTask<object> InvokeMethodAsync(
        HubInvocationContext invocationContext, Func<HubInvocationContext, ValueTask<object>> next)
    {
        Console.WriteLine($"Calling hub method '{invocationContext.HubMethodName}'");
        try
        {
            return await next(invocationContext);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception calling '{invocationContext.HubMethodName}': {ex}");
            throw;
        }
    }

    // Optional method
    public Task OnConnectedAsync(HubLifetimeContext context, Func<HubLifetimeContext, Task> next)
    {
        return next(context);
    }

    // Optional method
    public Task OnDisconnectedAsync(
        HubLifetimeContext context, Exception exception, Func<HubLifetimeContext, Exception, Task> next)
    {
        return next(context, exception);
    }
}

Filter sind Middleware sehr ähnlich. Die next-Methode ruft den nächsten Filter auf. Der letzte Filter ruft die Hubmethode auf. Filter können auch das Ergebnis aus dem Warten auf next speichern und Logik ausführen, nachdem die Hubmethode aufgerufen wurde, ehe das Ergebnis von next zurückgegeben wird.

Um einen Hubmethodenaufruf in einem Filter zu überspringen, lösen Sie eine Ausnahme des Typs HubException aus, anstatt next aufzurufen. Der Client erhält eine Fehlermeldung, wenn er ein Ergebnis erwartet hat.

Verwenden von Hubfiltern

Wenn Sie die Filterlogik schreiben, versuchen Sie, sie generisch zu gestalten, indem Sie Attribute für Hubmethoden verwenden, anstatt auf Namen von Hubmethoden zu prüfen.

Betrachten wir einen Filter, der ein Argument der Hubmethode auf unzulässige Formulierungen prüft und alle gefundenen Formulierungen durch *** ersetzt. In diesem Beispiel wird davon ausgegangen, dass eine LanguageFilterAttribute-Klasse definiert ist. Die Klasse verfügt über eine Eigenschaft namens FilterArgument, die bei Verwendung des Attributs festgelegt werden kann.

  1. Platzieren Sie das Attribut in der Hubmethode, die über ein zu bereinigende Zeichenfolgenargument verfügt:

    public class ChatHub
    {
        [LanguageFilter(filterArgument = 0)]
        public async Task SendMessage(string message, string username)
        {
            await Clients.All.SendAsync("SendMessage", $"{username} says: {message}");
        }
    }
    
  2. Definieren Sie einen Hubfilter, um auf das Attribut zu prüfen, und ersetzen Sie unzulässige Formulierungen in einem Hubmethodenargument durch ***:

    public class LanguageFilter : IHubFilter
    {
        // populated from a file or inline
        private List<string> bannedPhrases = new List<string> { "async void", ".Result" };
    
        public async ValueTask<object> InvokeMethodAsync(HubInvocationContext invocationContext, 
            Func<HubInvocationContext, ValueTask<object>> next)
        {
            var languageFilter = (LanguageFilterAttribute)Attribute.GetCustomAttribute(
                invocationContext.HubMethod, typeof(LanguageFilterAttribute));
            if (languageFilter != null &&
                invocationContext.HubMethodArguments.Count > languageFilter.FilterArgument &&
                invocationContext.HubMethodArguments[languageFilter.FilterArgument] is string str)
            {
                foreach (var bannedPhrase in bannedPhrases)
                {
                    str = str.Replace(bannedPhrase, "***");
                }
    
                var arguments = invocationContext.HubMethodArguments.ToArray();
                arguments[languageFilter.FilterArgument] = str;
                invocationContext = new HubInvocationContext(invocationContext.Context,
                    invocationContext.ServiceProvider,
                    invocationContext.Hub,
                    invocationContext.HubMethod,
                    arguments);
            }
    
            return await next(invocationContext);
        }
    }
    
  3. Registrieren Sie den Hubfilter in der Startup.ConfigureServices-Methode. Um zu vermeiden, dass die Liste der unzulässigen Formulierungen für jeden Aufruf neu initialisiert wird, wird der Hubfilter als Singleton registriert:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSignalR(hubOptions =>
        {
            hubOptions.AddFilter<LanguageFilter>();
        });
    
        services.AddSingleton<LanguageFilter>();
    }
    

Das HubInvocationContext-Objekt

HubInvocationContext enthält Informationen für den aktuellen Hubmethodenaufruf.

Eigenschaft Beschreibung des Dataflows Typ
Context HubCallerContext enthält Informationen zur Verbindung. HubCallerContext
Hub Die Instanz des Hubs, der für diesen Aufruf der Hubmethode verwendet wird. Hub
HubMethodName Der Name der aufgerufenen Hubmethode. string
HubMethodArguments Die Liste der Argumente, die an die Hubmethode übergeben werden. IReadOnlyList<string>
ServiceProvider Der bereichsbezogene Dienstanbieter für diesen Hubmethodenaufruf. IServiceProvider
HubMethod Die Informationen zur Hubmethode. MethodInfo

Das HubLifetimeContext-Objekt

HubLifetimeContext enthält Informationen für die Hubmethoden OnConnectedAsync und OnDisconnectedAsync.

Eigenschaft Beschreibung des Dataflows Typ
Context HubCallerContext enthält Informationen zur Verbindung. HubCallerContext
Hub Die Instanz des Hubs, der für diesen Hubmethodenaufruf verwendet wird. Hub
ServiceProvider Der bereichsbezogene Dienstanbieter für diesen Hubmethodenaufruf. IServiceProvider

Autorisierung und Filter

Autorisieren Sie Attribute für Hubmethoden, die vor Hubfiltern ausgeführt werden.