Compartilhar via


Usar filtros de hub no ASP.NET Core SignalR

Filtros de hub:

  • Estão disponíveis no ASP.NET Core 5.0 ou posterior.
  • Permitem que a lógica seja executada antes e depois que os métodos de hub são invocados pelos clientes.

Esse artigo fornece diretrizes para escrever e usar filtros de hub.

Configurar filtros de hub

Os filtros de hub podem ser aplicados globalmente ou por tipo de hub. A ordem na qual os filtros são adicionados é a ordem na qual os filtros são executados. Os filtros de hub global são executados antes dos filtros de hub local.

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>();
    });
}

Um filtro de hub pode ser adicionado de uma das seguintes maneiras:

  • Adicione um filtro por tipo concreto:

    hubOptions.AddFilter<TFilter>();
    

    Isso será resolvido por meio da injeção de dependência (DI) ou do tipo ativado.

  • Adicione um filtro por tipo de runtime:

    hubOptions.AddFilter(typeof(TFilter));
    

    Isso será resolvido a partir do DI ou do tipo ativado.

  • Adicione um filtro por instância:

    hubOptions.AddFilter(new MyFilter());
    

    Essa instância será usada como um banco de dados individual. Todas as invocações de método de hub usarão a mesma instância.

Os filtros de hub são criados e descartados por invocação de hub. Se você quiser armazenar o estado global no filtro ou nenhum estado, adicione o tipo de filtro de hub à DI como um banco de dados individual para obter um melhor desempenho. Como alternativa, adicione o filtro como uma instância se puder.

Criar filtros de hub

Crie um filtro declarando uma classe que herda de IHubFiltere adicione o método InvokeMethodAsync. Há também OnConnectedAsync e OnDisconnectedAsync que, opcionalmente, podem ser implementados para encapsular os métodos de hub OnConnectedAsync e OnDisconnectedAsync, respectivamente.

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);
    }
}

Os filtros são muito semelhantes ao middleware. O método next invoca o próximo filtro. O filtro final invocará o método hub. Os filtros também podem armazenar o resultado da lógica de espera next e da lógica de execução após o método de hub ter sido chamado antes de retornar o resultado de next.

Para ignorar uma invocação de método de hub em um filtro, gere uma exceção do tipo HubException em vez de chamar next. O cliente receberá um erro se esperava um resultado.

Usar filtros de hub

Ao escrever a lógica de filtro, tente torná-la genérica usando atributos em métodos de hub em vez de verificar nomes de método de hub.

Considere um filtro que verificará um argumento de método de hub para frases proibidas e substituirá todas as frases encontradas por ***. Para esse exemplo, suponha que uma classe LanguageFilterAttribute seja definida. A classe tem uma propriedade chamada FilterArgument que pode ser definida ao usar o atributo .

  1. Coloque o atributo no método de hub que tem um argumento de cadeia de caracteres a ser limpo:

    public class ChatHub
    {
        [LanguageFilter(filterArgument = 0)]
        public async Task SendMessage(string message, string username)
        {
            await Clients.All.SendAsync("SendMessage", $"{username} says: {message}");
        }
    }
    
  2. Defina um filtro de hub para marcar para o atributo e substitua frases proibidas em um argumento de método de hub por ***:

    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. Registre o filtro de hub no método Startup.ConfigureServices. Para evitar a reinicialização da lista de frases proibidas para cada invocação, o filtro de hub é registrado como um banco de dados individual:

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

O objeto HubInvocationContext

O HubInvocationContext contém informações para a invocação do método de hub atual.

Propriedade Descrição Type
Context O HubCallerContext contém informações sobre a conexão. HubCallerContext
Hub A instância do Hub que está sendo usada para essa invocação de método de hub. Hub
HubMethodName O nome do método de hub que está sendo invocado. string
HubMethodArguments A lista de argumentos que estão sendo passados para o método hub. IReadOnlyList<string>
ServiceProvider O provedor de serviços com escopo para essa invocação de método de hub. IServiceProvider
HubMethod As informações do método de hub. MethodInfo

O objeto HubLifetimeContext

O HubLifetimeContext contém informações para os OnConnectedAsync métodos de hub e OnDisconnectedAsync.

Propriedade Descrição Type
Context O HubCallerContext contém informações sobre a conexão. HubCallerContext
Hub A instância do Hub que está sendo usada para essa invocação de método de hub. Hub
ServiceProvider O provedor de serviços com escopo para essa invocação de método de hub. IServiceProvider

Autorização e filtros

A autorização de atributos em métodos de hub é executa antes dos filtros de hub.