docs/en/framework/infrastructure/interceptors.md
ABP provides a powerful interception system that allows you to execute custom logic before and after method calls without modifying the original method code. This is achieved through dynamic proxying and is extensively used throughout the ABP framework to implement cross-cutting concerns. ABP's interception is implemented on top of the Castle DynamicProxy library.
Interception is a technique that allows executing additional logic before or after a method call without directly modifying the method's code. This is achieved through dynamic proxying, where the runtime generates proxy classes that wrap the original class.
When a method is called on a proxied object:
This enables cross-cutting concerns (logic that applies across many parts of an application) to be handled in a clean, reusable way without code duplication.
If you are familiar with ASP.NET Core MVC, you've likely used action filters or page filters. Interceptors are conceptually similar but have some key differences:
ABP Framework extensively leverages interception to provide built-in features without requiring boilerplate code. Here are some key examples:
Automatically begins and commits/rolls back a database transaction when entering or exiting an application service method. This ensures data consistency without manual transaction management.
Input DTOs are automatically validated against data annotation attributes and custom validation rules before executing the service logic, providing consistent validation behavior across all services.
Checks user permissions before allowing the execution of application service methods, ensuring security policies are enforced consistently.
Checks if a feature is enabled before executing the service logic, allowing you to conditionally enable or restrict functionality for tenants or the application.
Automatically logs who performed an action, when it happened, what parameters were used, and what data was involved, providing comprehensive audit trails.
You can create custom interceptors in ABP to implement your own cross-cutting concerns.
Create a class that inherits from AbpInterceptor:
using System.Threading.Tasks;
using Volo.Abp.Aspects;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
public class ExecutionTimeLogInterceptor : AbpInterceptor, ITransientDependency
{
private readonly ILogger<ExecutionTimeLogInterceptor> _logger;
public ExecutionTimeLogInterceptor(ILogger<ExecutionTimeLogInterceptor> logger)
{
_logger = logger;
}
public override async Task InterceptAsync(IAbpMethodInvocation invocation)
{
var sw = Stopwatch.StartNew();
_logger.LogInformation($"Executing {invocation.TargetObject.GetType().Name}.{invocation.Method.Name}");
// Proceed to the actual target method
await invocation.ProceedAsync();
sw.Stop();
_logger.LogInformation($"Executed {invocation.TargetObject.GetType().Name}.{invocation.Method.Name} in {sw.ElapsedMilliseconds} ms");
}
}
Create a static class that contains the RegisterIfNeeded method and register the interceptor in the PreConfigureServices method of your module.
The ShouldIntercept method is used to determine if the interceptor should be registered for the given type. You can add an IExecutionTimeLogEnabled interface and implement it in the classes that you want to intercept.
DynamicProxyIgnoreTypesis static class that contains the types that should be ignored by the interceptor. See Performance Considerations for more information.
// Define an interface to mark the classes that should be intercepted
public interface IExecutionTimeLogEnabled
{
}
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
// A simple service that added to the DI container and will be intercepted since it implements the `IExecutionTimeLogEnabled` interface
public class SampleExecutionTimeService : IExecutionTimeLogEnabled, ITransientDependency
{
public virtual async Task DoWorkAsync()
{
// Simulate a long-running task to test the interceptor
await Task.Delay(1000);
}
}
using System;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
public static class ExecutionTimeLogInterceptorRegistrar
{
public static void RegisterIfNeeded(IOnServiceRegistredContext context)
{
if (ShouldIntercept(context.ImplementationType))
{
context.Interceptors.TryAdd<ExecutionTimeLogInterceptor>();
}
}
private static bool ShouldIntercept(Type type)
{
return !DynamicProxyIgnoreTypes.Contains(type) && typeof(IExecutionTimeLogEnabled).IsAssignableFrom(type);
}
}
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistered(ExecutionTimeLogInterceptorRegistrar.RegisterIfNeeded);
}
For best performance and reliability, implement your service methods as asynchronous to avoid async over sync, that can cause unexpected problems, For more information, see Should I expose synchronous wrappers for asynchronous methods?
For class proxies, methods need to be marked as virtual so that they can be overridden by the proxy. Otherwise, interception will not occur.
public class MyService : IExecutionTimeLogEnabled, ITransientDependency
{
// This method CANNOT be intercepted (not virtual)
public void CannotBeIntercepted()
{
}
// This method CAN be intercepted (virtual)
public virtual void CanBeIntercepted()
{
}
}
This restriction does not apply to interface-based proxies. If your service implements an interface and is injected via that interface, all methods can be intercepted regardless of the
virtualkeyword.
Interceptors only work when services are resolved from the dependency injection container. Direct instantiation with new bypasses interception:
// This will NOT be intercepted
var service = new MyService();
service.CannotBeIntercepted();
// This WILL be intercepted (if MyService is registered with DI)
var service = serviceProvider.GetService<MyService>();
service.CanBeIntercepted();
Interceptors are generally efficient, but each one adds method-call overhead. Keep the number of interceptors minimal on hot paths.
Castle DynamicProxy can negatively impact performance for certain components, notably ASP.NET Core MVC controllers. See the discussions in castleproject/Core#486 and abpframework/abp#3180.
ABP uses interceptors for features like UOW, auditing, and authorization, which rely on dynamic proxy classes. For controllers, prefer implementing cross-cutting concerns with middleware or MVC/Page filters instead of dynamic proxies.
To avoid generating dynamic proxies for specific types, use the static class DynamicProxyIgnoreTypes and add the base classes of the types to the list. Subclasses of any listed base class are also ignored. ABP framework already adds some base classes to the list (ComponentBase, ControllerBase, PageModel, ViewComponent); you can add more base classes if needed.
Always use interface-based proxies instead of class-based proxies for better performance.