From 223bb51bc0338599882bf0d96de90ae8d38ce311 Mon Sep 17 00:00:00 2001 From: selman koc <64414348+skoc10@users.noreply.github.com> Date: Wed, 3 Sep 2025 09:11:43 +0300 Subject: [PATCH 1/5] Update version to release 9.3.3 --- common.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common.props b/common.props index faccfb4394..bfed4a9d81 100644 --- a/common.props +++ b/common.props @@ -1,8 +1,8 @@ latest - 9.3.2 - 4.3.2 + 9.3.3 + 4.3.3 $(NoWarn);CS1591;CS0436 https://abp.io/assets/abp_nupkg.png https://abp.io/ From 631865ab1982404bc3cbd48b103da369e5d20691 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 2 Sep 2025 20:29:42 +0800 Subject: [PATCH 2/5] Add documentation for ABP interceptors Resolve #663 --- docs/en/docs-nav.json | 4 + .../framework/infrastructure/interceptors.md | 186 ++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 docs/en/framework/infrastructure/interceptors.md diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index dc2412b529..ae2a268ab5 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -749,6 +749,10 @@ "text": "JSON", "path": "framework/infrastructure/json.md" }, + { + "text": "Interceptors", + "path": "framework/infrastructure/interceptors.md" + }, { "text": "Object to Object Mapping", "path": "framework/infrastructure/object-to-object-mapping.md" diff --git a/docs/en/framework/infrastructure/interceptors.md b/docs/en/framework/infrastructure/interceptors.md new file mode 100644 index 0000000000..42e9e14b61 --- /dev/null +++ b/docs/en/framework/infrastructure/interceptors.md @@ -0,0 +1,186 @@ +# Interceptors + +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. + +## What is Dynamic Proxying / Interception? + +**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: +1. The call is intercepted by the proxy +2. Custom behaviors (like logging, validation, or authorization) can be executed +3. The original method is called +4. Additional logic can be executed after the method completes + +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. + +## Similarities and Differences with MVC Action/Page Filters + +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: + +### Similarities + +* Both allow executing code before and after method execution +* Both are used to implement cross-cutting concerns like validation, logging, caching, or exception handling +* Both support asynchronous operations + +### Differences + +* **Scope**: Filters are tied to MVC's request pipeline, while interceptors can be applied to **any class or service** in the application +* **Configuration**: Filters are configured via attributes or middleware in MVC, while interceptors in ABP are applied through **dependency injection and dynamic proxies** +* **Target**: Interceptors can target application services, domain services, repositories, and virtually any service resolved from the IoC container—not just web controllers + +## How ABP Uses Interceptors + +ABP Framework extensively leverages interception to provide built-in features without requiring boilerplate code. Here are some key examples: + +### Unit of Work (UOW) + +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 Validation + +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. + +### Authorization + +Checks user permissions before allowing the execution of application service methods, ensuring security policies are enforced consistently. + +### Feature & Global Feature Checking + +Checks if a feature is enabled before executing the service logic, allowing you to conditionally enable or restrict functionality for tenants or the application. + +### Auditing + +Automatically logs who performed an action, when it happened, what parameters were used, and what data was involved, providing comprehensive audit trails. + +## Building Your Own Interceptor + +You can create custom interceptors in ABP to implement your own cross-cutting concerns. + +### Creating an Interceptor + +Create a class that inherits from `AbpInterceptor`: + +````csharp +using System.Threading.Tasks; +using Volo.Abp.Aspects; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; + +public class ExecutionTimeLogInterceptor : AbpInterceptor, ITransientDependency +{ + private readonly ILogger _logger; + + public ExecutionTimeLogInterceptor(ILogger 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"); + } +} +```` + +### Register Interceptors + +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. + +> `DynamicProxyIgnoreTypes` is static class that contains the types that should be ignored by the interceptor. See [Performance Considerations](#performance-considerations) for more information. + +````csharp +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(); + } + } + + private static bool ShouldIntercept(Type type) + { + return !DynamicProxyIgnoreTypes.Contains(type) && typeof(IExecutionTimeLogEnabled).IsAssignableFrom(type); + } +} +```` + +````csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + context.Services.OnRegistered(ExecutionTimeLogInterceptor.RegisterIfNeeded); +} +```` + +## Restrictions and Important Notes + +### Virtual Methods Requirement + +For **class proxies**, methods need to be marked as `virtual` so that they can be overridden by the proxy. Otherwise, interception will not occur. + +````csharp +public class MyService : 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 `virtual` keyword. + +### Dependency Injection Scope + +Interceptors only work when services are resolved from the dependency injection container. Direct instantiation with `new` bypasses interception: + +````csharp +// 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(); +service.CanBeIntercepted(); +```` + +### Performance Considerations + +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](https://github.com/castleproject/Core/issues/486) and [abpframework/abp#3180](https://github.com/abpframework/abp/issues/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. + +## See Also + +* [Castle DynamicProxy](https://github.com/castleproject/Core/blob/master/docs/dynamicproxy.md) +* [Castle.Core.AsyncInterceptor](https://github.com/JSkimming/Castle.Core.AsyncInterceptors) +* [ASP.NET Core Filters](https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters) From 6f0794b12d0382559892536fcd1938d357d53d71 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 2 Sep 2025 20:32:42 +0800 Subject: [PATCH 3/5] Remove placeholder for dynamic proxying docs --- docs/en/dynamic-proxying-interceptors.md | 7 ------- docs/en/framework/infrastructure/interceptors.md | 1 + 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 docs/en/dynamic-proxying-interceptors.md diff --git a/docs/en/dynamic-proxying-interceptors.md b/docs/en/dynamic-proxying-interceptors.md deleted file mode 100644 index 722eadc249..0000000000 --- a/docs/en/dynamic-proxying-interceptors.md +++ /dev/null @@ -1,7 +0,0 @@ -# Dynamic Proxying / Interceptors - -This document is planned to be written later. - -## See Also - -* [Video tutorial](https://abp.io/video-courses/essentials/interception) \ No newline at end of file diff --git a/docs/en/framework/infrastructure/interceptors.md b/docs/en/framework/infrastructure/interceptors.md index 42e9e14b61..261b1026e6 100644 --- a/docs/en/framework/infrastructure/interceptors.md +++ b/docs/en/framework/infrastructure/interceptors.md @@ -184,3 +184,4 @@ To avoid generating dynamic proxies for specific types, use the static class `Dy * [Castle DynamicProxy](https://github.com/castleproject/Core/blob/master/docs/dynamicproxy.md) * [Castle.Core.AsyncInterceptor](https://github.com/JSkimming/Castle.Core.AsyncInterceptors) * [ASP.NET Core Filters](https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters) +* [ABP Video Introduction](https://abp.io/video-courses/essentials/interception) From 399613fc6aaad76150308e3248759819beee989f Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 4 Sep 2025 14:41:58 +0800 Subject: [PATCH 4/5] Reorder JSON section in docs navigation --- docs/en/docs-nav.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index ae2a268ab5..585309d8eb 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -745,14 +745,14 @@ "text": "Image Manipulation", "path": "framework/infrastructure/image-manipulation.md" }, - { - "text": "JSON", - "path": "framework/infrastructure/json.md" - }, { "text": "Interceptors", "path": "framework/infrastructure/interceptors.md" }, + { + "text": "JSON", + "path": "framework/infrastructure/json.md" + }, { "text": "Object to Object Mapping", "path": "framework/infrastructure/object-to-object-mapping.md" From 94ece019965e188eba8228cbcb74e03d43dd6fce Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 4 Sep 2025 15:02:28 +0800 Subject: [PATCH 5/5] Update interceptor docs with links and code improvements --- .../framework/infrastructure/interceptors.md | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/docs/en/framework/infrastructure/interceptors.md b/docs/en/framework/infrastructure/interceptors.md index 261b1026e6..d2f0541411 100644 --- a/docs/en/framework/infrastructure/interceptors.md +++ b/docs/en/framework/infrastructure/interceptors.md @@ -1,6 +1,6 @@ # Interceptors -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. +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](https://www.castleproject.org/projects/dynamicproxy/) library. ## What is Dynamic Proxying / Interception? @@ -34,23 +34,23 @@ If you are familiar with ASP.NET Core MVC, you've likely used **action filters** ABP Framework extensively leverages interception to provide built-in features without requiring boilerplate code. Here are some key examples: -### Unit of Work (UOW) +### [Unit of Work (UOW)](../architecture/domain-driven-design/unit-of-work.md) 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 Validation +### [Input Validation](../fundamentals/validation.md) 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. -### Authorization +### [Authorization](../fundamentals/authorization.md) Checks user permissions before allowing the execution of application service methods, ensuring security policies are enforced consistently. -### Feature & Global Feature Checking +### [Feature](./features.md) & [Global Feature](./global-features.md) Checking Checks if a feature is enabled before executing the service logic, allowing you to conditionally enable or restrict functionality for tenants or the application. -### Auditing +### [Auditing](./audit-logging.md) Automatically logs who performed an action, when it happened, what parameters were used, and what data was involved, providing comprehensive audit trails. @@ -101,6 +101,13 @@ The `ShouldIntercept` method is used to determine if the interceptor should be r > `DynamicProxyIgnoreTypes` is static class that contains the types that should be ignored by the interceptor. See [Performance Considerations](#performance-considerations) for more information. +````csharp +// Define an interface to mark the classes that should be intercepted +public interface IExecutionTimeLogEnabled +{ +} +```` + ````csharp using System; using Volo.Abp.DependencyInjection; @@ -112,7 +119,7 @@ public static class ExecutionTimeLogInterceptorRegistrar { if (ShouldIntercept(context.ImplementationType)) { - context.Interceptors.TryAdd(); + context.Interceptors.TryAdd(); } } @@ -181,7 +188,7 @@ To avoid generating dynamic proxies for specific types, use the static class `Dy ## See Also -* [Castle DynamicProxy](https://github.com/castleproject/Core/blob/master/docs/dynamicproxy.md) +* [Video tutorial: Interceptors in ABP Framework](https://abp.io/video-courses/essentials/interception) +* [Castle DynamicProxy](https://www.castleproject.org/projects/dynamicproxy/) * [Castle.Core.AsyncInterceptor](https://github.com/JSkimming/Castle.Core.AsyncInterceptors) * [ASP.NET Core Filters](https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters) -* [ABP Video Introduction](https://abp.io/video-courses/essentials/interception)