Interceptor
Interceptors can intercede additional processing before or after the any published command has been passed and consumed to subscribers.
Use interceptor
There are three levels to enable interceptor.
- Apply globally to the
Router
(IPublisher
,ISubscribable
) - Apply it to all methods in the
[Routes]
class. - Apply only to specific handler.
- Apply the
[Route]
method - Apply
.Subscribe(...)
,.SubscribeAwait(...)
// 1. Apply globally to the router.
Router.Default.AddFilter(new YourInterceptor());
var router = new Router();
router.AddFilter(new YourInterceptor());
IPublisher pubilsher = router;
publisher.AddFilter(new YourInterceptor());
ISubscribable subscribable = router;
subscribable.AddFilter(new YourInterceptor());
// 1. Apply to the router with VContaienr.
builder.RegisterVitalRouter(routing =>
{
routing.Filters.Add<Logging>();
routing.Filters.Add<ErrorHandling>();
});
// 1. Apply to the router with Microsoft.Extensions.DependencyInjection.
builder.AddVitalRouter(routing =>
{
routing.Filters.Add<Logging>();
routing.Filters.Add<ErrorHandling>();
});
[Routes]
[Filter(typeof(Logging))] // 2. Apply to the type
public partial class FooPresenter
{
[Filter(typeof(ExtraInterceptor))] // 3. Apply to the method
public void On(CommandA cmd)
{
// ...
}
}
All of these are executed in the order in which they are registered, from top to bottom.
If you take the way of 2 or 3, the Interceptor instance is resolved as follows.
- If you are using DI, the DI container will resolve this automatically.
- if you are not using DI, you will need to pass the instance in the
MapTo
call.-
MapTo(Router.Default, new Logging(), new ErrorHandling());
-
// auto-generated
public Subscription MapTo(ICommandSubscribable subscribable, Logging interceptor1, ErrorHandling interceptor2) { /* ... */ }
-
Create custom interceptor
Arbitrary interceptors can be created by implementing ICommandInterceptor
.
Example 1: Some kind of processing is interspersed before and after the command is consumed.
class Logging : ICommandInterceptor
{
public async ValueTask InvokeAsync<T>(
T command,
PublishContext context,
PublishCOntinuation<T> next)
where T : ICommand
{
UnityEngine.Debug.Log($"Start {typeof(T)}");
// Execute subsequent routes.
await next(command, context);
UnityEngine.Debug.Log($"End {typeof(T)}");
}
}
Example 2: try/catch all subscribers exceptions.
class ExceptionHandling : ICommandInterceptor
{
public async ValueTask InvokeAsync<T>(
T command,
PublishContext context,
PublishContinuation<T> next)
where T : ICommand
{
try
{
await next(command, context);
}
catch (Exception ex)
{
// Error tracking you like
UnityEngine.Debug.Log($"oops! {ex.Message}");
}
}
}
Example 3: Filtering command.
class MyFilter : ICommandInterceptor
{
public async ValueTask InvokeAsync<T>(
T command,
PublishContext context,
PublishContinuation<T> next)
where T : ICommand
{
if (command is FooCommand { X: > 100 } cmd)
{
// Deny. Skip the rest of the subscribers.
return;
}
// Allow.
await next(command, context);
}
}