Share via


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에 싱글톤으로 추가합니다. 또는 가능한 경우 필터를 인스턴스로 추가합니다.

허브 필터 만들기

IHubFilter에서 상속되는 클래스를 선언하여 필터를 만들고 InvokeMethodAsync 메서드를 추가합니다. OnConnectedAsyncOnDisconnectedAsync 허브 메서드를 각각 래핑하기 위해 필요에 따라 구현할 수 있는 OnConnectedAsyncOnDisconnectedAsync도 있습니다.

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의 결과 및 실행 논리를 저장할 수도 있습니다.

필터에서 허브 메서드 호출을 건너뛰려면 next를 호출하는 대신 HubException 형식의 예외를 throw합니다. 결과가 예상되면 클라이언트에서 오류가 발생합니다.

허브 필터 사용

필터 논리를 작성할 때 허브 메서드 이름을 확인하는 대신 허브 메서드의 특성을 사용하여 제네릭으로 만듭니다.

허브 메서드 인수에서 금지된 구를 확인하고 찾은 모든 구를 ***로 바꾸는 필터를 고려합니다. 이 예제에서는 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에는 현재 허브 메서드 호출에 대한 정보가 포함되어 있습니다.

속성 설명 Type
Context HubCallerContext에는 연결에 대한 정보가 포함되어 있습니다. HubCallerContext
Hub 이 허브 메서드 호출에 사용되는 허브의 인스턴스입니다. Hub
HubMethodName 호출되는 허브 메서드의 이름입니다. string
HubMethodArguments 허브 메서드에 전달되는 인수 목록입니다. IReadOnlyList<string>
ServiceProvider 이 허브 메서드 호출에 대한 범위가 지정된 서비스 공급자입니다. IServiceProvider
HubMethod 허브 메서드 정보입니다. MethodInfo

HubLifetimeContext 개체

HubLifetimeContext에는 OnConnectedAsyncOnDisconnectedAsync 허브 메서드에 대한 정보가 포함되어 있습니다.

속성 설명 Type
Context HubCallerContext에는 연결에 대한 정보가 포함되어 있습니다. HubCallerContext
Hub 이 허브 메서드 호출에 사용되는 허브의 인스턴스입니다. Hub
ServiceProvider 이 허브 메서드 호출에 대한 범위가 지정된 서비스 공급자입니다. IServiceProvider

권한 부여 및 필터

허브 필터 전에 실행되는 허브 메서드의 특성에 권한을 부여합니다.