Поделиться через


Использование фильтров концентратора в ASP.NET Core SignalR

Фильтры концентратора:

  • Доступны в ASP.NET Core 5.0 или более поздней версии.
  • Разрешить выполнение логики до и после вызова методов концентратора клиентами.

В этой статье приводятся рекомендации по написанию и использованию фильтров концентратора.

Настройка фильтров концентратора

Фильтры концентратора можно применять глобально или на один тип концентратора. Порядок добавления фильтров — это порядок выполнения фильтров. Глобальные фильтры концентратора выполняются до локальных фильтров концентратора.

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

Фильтр концентратора можно добавить одним из следующих способов:

  • Добавьте фильтр по конкретному типу:

    hubOptions.AddFilter<TFilter>();
    

    Это будет разрешено из внедрения зависимостей (DI) или типа, активированного.

  • Добавьте фильтр по типу среды выполнения:

    hubOptions.AddFilter(typeof(TFilter));
    

    Это будет разрешено из di или типа активировано.

  • Добавление фильтра по экземпляру:

    hubOptions.AddFilter(new MyFilter());
    

    Этот экземпляр будет использоваться как одноэлементный. Все вызовы методов концентратора будут использовать один и тот же экземпляр.

Фильтры концентратора создаются и удаляются для каждого вызова концентратора. Если вы хотите сохранить глобальное состояние в фильтре или нет состояния, добавьте тип фильтра концентратора в di в качестве одного из вариантов для повышения производительности. Кроме того, добавьте фильтр в качестве экземпляра, если это возможно.

Создание фильтров концентратора

Создайте фильтр, объявив класс, наследуемый InvokeMethodAsync от IHubFilterи добавьте метод. Существует также OnConnectedAsync и OnDisconnectedAsync то, что может быть реализовано при необходимости для упаковки OnConnectedAsync методов и OnDisconnectedAsync концентраторов соответственно.

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

Фильтры очень похожи на ПО промежуточного слоя. Метод next вызывает следующий фильтр. Последний фильтр вызовет метод концентратора. Фильтры также могут хранить результат ожидания next и выполнения логики после вызова метода концентратора, прежде чем возвращать результат из next.

Чтобы пропустить вызов метода концентратора в фильтре, создайте исключение типа HubException вместо вызова next. Клиент получит ошибку, если он ожидал результата.

Использование фильтров концентратора

При написании логики фильтра попробуйте сделать его универсальным с помощью атрибутов в методах концентратора вместо проверка имен методов концентратора.

Рассмотрим фильтр, который будет проверка аргумент метода концентратора для запрещенных фраз и замените любые фразы, на которые он находит***. В этом примере предполагается, что LanguageFilterAttribute класс определен. Класс имеет свойство с именем FilterArgument , которое можно задать при использовании атрибута.

  1. Поместите атрибут в метод концентратора, имеющий строковый аргумент для очистки:

    public class ChatHub
    {
        [LanguageFilter(filterArgument = 0)]
        public async Task SendMessage(string message, string username)
        {
            await Clients.All.SendAsync("SendMessage", $"{username} says: {message}");
        }
    }
    
  2. Определите фильтр концентратора для проверка атрибута и замените запрещенные фразы в аргументе метода концентратора следующим ***образом:

    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. Зарегистрируйте фильтр концентратора в методе Startup.ConfigureServices . Чтобы избежать повторной инициализации списка запрещенных фраз для каждого вызова, фильтр концентратора регистрируется как одноэлементный:

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

Объект HubInvocationContext

Содержит HubInvocationContext сведения о вызове текущего метода концентратора.

Свойство Описание Тип
Context Содержит HubCallerContext сведения о подключении. HubCallerContext
Hub Экземпляр концентратора, используемого для вызова этого метода концентратора. Hub
HubMethodName Имя вызываемого метода концентратора. string
HubMethodArguments Список аргументов, передаваемых в метод концентратора. IReadOnlyList<string>
ServiceProvider Поставщик услуг область для вызова этого метода концентратора. IServiceProvider
HubMethod Сведения о методе концентратора. MethodInfo

Объект HubLifetimeContext

Содержит HubLifetimeContext сведения о методах и OnDisconnectedAsync методах концентратораOnConnectedAsync.

Свойство Описание Тип
Context Содержит HubCallerContext сведения о подключении. HubCallerContext
Hub Экземпляр концентратора, используемого для вызова этого метода концентратора. Hub
ServiceProvider Поставщик услуг область для вызова этого метода концентратора. IServiceProvider

Авторизация и фильтры

Авторизация атрибутов в методах концентратора, выполняемых до фильтров концентратора.