Nguon: Microsoft Learn · .NET 8.0

Sử dụng Hub Filter trong ASP.NET Core SignalR

Nguồn: Use hub filters in ASP.NET Core SignalR

Hub filter (bộ lọc hub):

Bài viết này cung cấp hướng dẫn viết và sử dụng hub filter.

Cấu hình hub filter

Hub filter có thể được áp dụng toàn cục (globally) hoặc theo từng loại hub. Thứ tự thêm filter là thứ tự các filter chạy. Các global hub filter chạy trước local hub filter.

csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR(options =>
    {
        // Global filter sẽ chạy trước
        options.AddFilter<CustomFilter>();
    }).AddHubOptions<ChatHub>(options =>
    {
        // Local filter sẽ chạy sau
        options.AddFilter<CustomFilter2>();
    });
}

Một hub filter có thể được thêm theo một trong các cách sau:

``csharp hubOptions.AddFilter<TFilter>(); ``

Cái này sẽ được resolve từ dependency injection (DI) hoặc kích hoạt theo kiểu (type activated).

``csharp hubOptions.AddFilter(typeof(TFilter)); ``

Cái này sẽ được resolve từ DI hoặc kích hoạt theo kiểu.

``csharp hubOptions.AddFilter(new MyFilter()); ``

Instance này sẽ được sử dụng như một singleton. Tất cả các lần gọi phương thức hub sẽ sử dụng cùng một instance.

Hub filter được tạo và hủy cho mỗi lần gọi hub. Nếu bạn muốn lưu trữ trạng thái toàn cục trong filter, hoặc không có trạng thái, hãy thêm kiểu hub filter vào DI dưới dạng singleton để có hiệu suất tốt hơn. Ngoài ra, hãy thêm filter như một instance nếu có thể.

Tạo hub filter

Tạo filter bằng cách khai báo một class kế thừa từ IHubFilter, và thêm phương thức InvokeMethodAsync. Ngoài ra còn có OnConnectedAsyncOnDisconnectedAsync có thể tùy chọn triển khai để bao bọc các phương thức hub OnConnectedAsyncOnDisconnectedAsync tương ứng.

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

    // Phương thức tùy chọn
    public Task OnConnectedAsync(HubLifetimeContext context, Func<HubLifetimeContext, Task> next)
    {
        return next(context);
    }

    // Phương thức tùy chọn
    public Task OnDisconnectedAsync(
        HubLifetimeContext context, Exception exception, Func<HubLifetimeContext, Exception, Task> next)
    {
        return next(context, exception);
    }
}

Filter rất giống với middleware (phần mềm trung gian). Phương thức next gọi filter tiếp theo. Filter cuối cùng sẽ gọi phương thức hub. Filter cũng có thể lưu trữ kết quả từ việc await next và chạy logic sau khi phương thức hub đã được gọi trước khi trả về kết quả từ next.

Để bỏ qua lần gọi phương thức hub trong filter, hãy ném exception kiểu HubException thay vì gọi next. Client sẽ nhận được lỗi nếu nó đang mong đợi kết quả.

Sử dụng hub filter

Khi viết logic filter, hãy cố gắng làm cho nó chung chung bằng cách sử dụng attribute trên các phương thức hub thay vì kiểm tra tên phương thức hub.

Hãy xem xét một filter sẽ kiểm tra đối số của phương thức hub để tìm các cụm từ bị cấm và thay thế bất kỳ cụm từ nào tìm thấy bằng ***. Ví dụ này giả định một class LanguageFilterAttribute được định nghĩa. Class có thuộc tính FilterArgument có thể được thiết lập khi sử dụng attribute.

  1. Đặt attribute trên phương thức hub có đối số chuỗi cần được làm sạch:

``csharp public class ChatHub { [LanguageFilter(filterArgument = 0)] public async Task SendMessage(string message, string username) { await Clients.All.SendAsync("SendMessage", $"{username} says: {message}"); } } ``

  1. Định nghĩa một hub filter để kiểm tra attribute và thay thế các cụm từ bị cấm trong đối số phương thức hub bằng ***:

```csharp public class LanguageFilter : IHubFilter { // Được điền từ file hoặc khai báo nội tuyến 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); } } ```

  1. Đăng ký hub filter trong phương thức Startup.ConfigureServices. Để tránh khởi tạo lại danh sách cụm từ bị cấm cho mỗi lần gọi, hub filter được đăng ký dưới dạng singleton:

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

services.AddSingleton<LanguageFilter>(); } ```

Đối tượng HubInvocationContext

HubInvocationContext chứa thông tin về lần gọi phương thức hub hiện tại.

Thuộc tínhMô tảKiểu
ContextHubCallerContext chứa thông tin về kết nối.HubCallerContext
HubInstance của Hub được sử dụng cho lần gọi phương thức hub này.Hub
HubMethodNameTên phương thức hub đang được gọi.string
HubMethodArgumentsDanh sách các đối số được truyền vào phương thức hub.IReadOnlyList<string>
ServiceProviderService provider theo phạm vi (scoped) cho lần gọi phương thức hub này.IServiceProvider
HubMethodThông tin phương thức hub.MethodInfo

Đối tượng HubLifetimeContext

HubLifetimeContext chứa thông tin cho các phương thức hub OnConnectedAsyncOnDisconnectedAsync.

Thuộc tínhMô tảKiểu
ContextHubCallerContext chứa thông tin về kết nối.HubCallerContext
HubInstance của Hub được sử dụng cho lần gọi phương thức hub này.Hub
ServiceProviderService provider theo phạm vi (scoped) cho lần gọi phương thức hub này.IServiceProvider

Phân quyền và filter

Authorize attribute trên các phương thức hub chạy trước hub filter.