Uso de filtros de concentrador en ASP.NET Core SignalR
Filtros de concentrador:
- Están disponibles en ASP.NET Core 5.0 o versiones posteriores.
- Permitir que la lógica se ejecute antes y después de que los clientes invoquen los métodos de concentrador.
En este artículo se proporcionan instrucciones para escribir y usar filtros de concentrador.
Configuración de filtros de concentrador
Los filtros de concentrador se pueden aplicar globalmente o por tipo de concentrador. El orden en el que se agregan filtros es el orden en el que se ejecutan los filtros. Los filtros de concentrador globales se ejecutan antes que los filtros de concentrador locales.
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>();
});
}
Se puede agregar un filtro de concentrador de una de las maneras siguientes:
Agregue un filtro por tipo concreto:
hubOptions.AddFilter<TFilter>();
Esto se resolverá a partir de la inserción de dependencias (DI) o el tipo activado.
Agregue un filtro por tipo de tiempo de ejecución:
hubOptions.AddFilter(typeof(TFilter));
Esto se resolverá desde la inserción de dependencias o el tipo activado.
Agregue un filtro por instancia:
hubOptions.AddFilter(new MyFilter());
Esta instancia se usará como singleton. Todas las invocaciones del método de concentrador usarán la misma instancia.
Los filtros de concentrador se crean y eliminan por invocación de concentrador. Si desea almacenar el estado global en el filtro o ningún estado, agregue el tipo de filtro de concentrador a DI como singleton para mejorar el rendimiento. Como alternativa, agregue el filtro como una instancia si puede.
Creación de filtros de concentrador
Cree un filtro declarando una clase que herede de IHubFilter
y agregue el método InvokeMethodAsync
. También hay OnConnectedAsync
y OnDisconnectedAsync
que se pueden implementar opcionalmente para encapsular los métodos de concentrador OnConnectedAsync
y 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);
}
}
Los filtros son muy similares al middleware. El método next
invoca el siguiente filtro. El filtro final invocará el método de concentrador. Los filtros también pueden almacenar el resultado de esperar next
y ejecutar la lógica después de llamar al método de concentrador antes de devolver el resultado de next
.
Para omitir una invocación de método de concentrador en un filtro, inicie una excepción de tipo HubException
en lugar de llamar a next
. El cliente recibirá un error si esperaba un resultado.
Uso de filtros de concentrador
Al escribir la lógica de filtro, intente convertirlo en genérico mediante el uso de atributos en métodos de concentrador en lugar de comprobar si hay nombres de método de concentrador.
Considere un filtro que comprobará un argumento de método de concentrador para las frases prohibidas y reemplazará las frases que encuentre por ***
.
En este ejemplo, supongamos que se define una clase LanguageFilterAttribute
. La clase tiene una propiedad denominada FilterArgument
que se puede establecer al usar el atributo.
Coloque el atributo en el método de concentrador que tiene un argumento de cadena que se va a limpiar:
public class ChatHub { [LanguageFilter(filterArgument = 0)] public async Task SendMessage(string message, string username) { await Clients.All.SendAsync("SendMessage", $"{username} says: {message}"); } }
Defina un filtro de concentrador para comprobar el atributo y reemplazar las frases prohibidas en un argumento de método de concentrador 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); } }
Registre el filtro de concentrador en el método
Startup.ConfigureServices
. Para evitar reinicializar la lista de frases prohibidas para cada invocación, el filtro de concentrador se registra como singleton:public void ConfigureServices(IServiceCollection services) { services.AddSignalR(hubOptions => { hubOptions.AddFilter<LanguageFilter>(); }); services.AddSingleton<LanguageFilter>(); }
El objeto HubInvocationContext
HubInvocationContext
contiene información para la invocación del método de concentrador actual.
Propiedad | Descripción | Tipo |
---|---|---|
Context |
HubCallerContext contiene información sobre la conexión. |
HubCallerContext |
Hub |
Instancia del concentrador que se usa para esta invocación de método de concentrador. | Hub |
HubMethodName |
El nombre del método de concentrador que se va a invocar. | string |
HubMethodArguments |
Lista de argumentos que se pasan al método de concentrador. | IReadOnlyList<string> |
ServiceProvider |
Proveedor de servicios con ámbito para esta invocación de método de concentrador. | IServiceProvider |
HubMethod |
Información del método de concentrador. | MethodInfo |
El objeto HubLifetimeContext
HubLifetimeContext
contiene información para los métodos de concentrador OnConnectedAsync
y OnDisconnectedAsync
.
Propiedad | Descripción | Tipo |
---|---|---|
Context |
HubCallerContext contiene información sobre la conexión. |
HubCallerContext |
Hub |
Instancia del concentrador que se usa para esta invocación de método de concentrador. | Hub |
ServiceProvider |
Proveedor de servicios con ámbito para esta invocación de método de concentrador. | IServiceProvider |
Autorización y filtros
Los atributos de autorización en los métodos de concentrador se ejecutan antes de los filtros de concentrador.