Skip to main content

use-interceptors-and-dynamic-proxy

ABP cross-cutting behavior via Castle dynamic proxies and IAbpInterceptor. USE FOR: how validation, unit of work, authorization, auditing, feature interceptors are selected; interceptor not running; custom interceptor via Services.OnRegistered. DO NOT USE FOR: module lifecycle (define-application-modules); audit-log persistence (configure-audit-logging); feature values (manage-settings-and-features); services without interception (register-and-replace-services).

설치로 이동

소스 정보

저장소
abpframework/abp-skills
최근 소스 활동
2026년 7월 20일 03:24
감지된 SKILL.md 언어
영어
스타
18
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
use-interceptors-and-dynamic-proxy
description
ABP cross-cutting behavior via Castle dynamic proxies and IAbpInterceptor. USE FOR: how validation, unit of work, authorization, auditing, feature interceptors are selected; interceptor not running; custom interceptor via Services.OnRegistered. DO NOT USE FOR: module lifecycle (define-application-modules); audit-log persistence (configure-audit-logging); feature values (manage-settings-and-features); services without interception (register-and-replace-services).
license
MIT
# Using Interceptors and Dynamic Proxies ABP attaches `IAbpInterceptor` implementations while services are registered, then the Autofac/Castle integration creates either an interface proxy or a class proxy. The call must cross that proxy for an interceptor to run. ## When to Use - Diagnose why validation, `[UnitOfWork]`, `[Authorize]`, `[Audited]`, or `[RequiresFeature]` is not taking effect on a service call. - Add reusable before/after behavior around application or domain services. - Select services for interception through `IServiceCollection.OnRegistered`. - Inspect the invoked method, arguments, target, or return value through `IAbpMethodInvocation`. ## When Not to Use - **Module lifecycle and ordinary registrations** — use define-application-modules. - **Audit-log storage, contributors, and entity history** — use configure-audit-logging. - **Feature definitions and values** — use manage-settings-and-features. - **Service exposure, replacement, keyed services, or decoration** — use register-and-replace-services. ## How it works ### The invocation contract Implement `IAbpInterceptor.InterceptAsync(IAbpMethodInvocation)` directly or derive from `AbpInterceptor`. Call `ProceedAsync()` exactly once to continue the chain. The invocation also exposes `Arguments`, `ArgumentsDictionary`, `GenericArguments`, `TargetObject`, `Method`, and `ReturnValue`. ```csharp using System.Diagnostics; using Microsoft.Extensions.Logging; using Volo.Abp.DependencyInjection; using Volo.Abp.DynamicProxy; public class ExecutionTimeInterceptor : AbpInterceptor, ITransientDependency { private readonly ILogger<ExecutionTimeInterceptor> _logger; public ExecutionTimeInterceptor(ILogger<ExecutionTimeInterceptor> logger) { _logger = logger; } public override async Task InterceptAsync(IAbpMethodInvocation invocation) { var stopwatch = Stopwatch.StartNew(); try { await invocation.ProceedAsync(); } finally { _logger.LogInformation( "{MethodName} completed in {ElapsedMilliseconds} ms", invocation.Method.Name, stopwatch.ElapsedMilliseconds); } } } ``` Omitting `ProceedAsync()` intentionally short-circuits the target and all later interceptors. Use `try/finally` when after-logic must run for both success and failure. ### Register a custom interceptor Choose an explicit marker or attribute. A marker interface keeps the selector simple and avoids intercepting unrelated services. ```csharp using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Collections; using Volo.Abp.DependencyInjection; public interface IExecutionTimeEnabled { } public static class ExecutionTimeInterceptorRegistrar { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { if (typeof(IExecutionTimeEnabled).IsAssignableFrom(context.ImplementationType)) { context.Interceptors.TryAdd<ExecutionTimeInterceptor>(); } } } public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.OnRegistered(ExecutionTimeInterceptorRegistrar.RegisterIfNeeded); } ``` `ExecutionTimeInterceptor` is conventionally registered because it implements `ITransientDependency`. Register the `OnRegistered` callback early enough to observe the target services; `PreConfigureServices` is the safe module-level location. ### How the built-in interceptors are selected Each built-in module installs an `OnRegistered` callback. Its registrar tests the implementation type and adds an interceptor only when needed: | Concern | Registrar trigger | | --- | --- | | Validation | Type implements `IValidationEnabled` and is not in `DynamicProxyIgnoreTypes`. | | Unit of work | `UnitOfWorkHelper.IsUnitOfWorkType(...)` returns true and the type is not ignored. | | Authorization | Type or any method has `[Authorize]` and the type is not ignored. | | Auditing | The type is auditable by default, has `[Audited]`, implements `IAuditingEnabled`, or has an audited method, subject to ignore rules. | | Features | Type or any method has `[RequiresFeature]` and the type is not ignored. | The registrar decision only attaches the interceptor to the service registration. The interceptor itself performs the runtime check and behavior. ### Hard conditions for interception All of these must hold: 1. The implementation is registered through ABP DI while the relevant `OnRegistered` callback is active. 2. The service is resolved from DI and invoked through the resolved proxy; `new` bypasses the proxy. 3. The registrar selects the implementation and adds the interceptor type. 4. The call uses an exposed interface, **or** the class-proxy target method is overridable (`virtual`, non-sealed class). 5. Class interception has not been disabled with `DisableAbpClassInterceptors`, a selector, or `DisableAbpFeaturesAttribute.DisableInterceptors`. 6. The Autofac integration is present. `AbpAutofacModule` depends on `AbpCastleCoreModule`; the latter registers the async interceptor adapter. For an interface proxy, inject and call the interface. For a class proxy, make the intercepted public/protected method `virtual`. A non-virtual class method executes normally but Castle cannot override it. ## Validation - Resolve the target from an `IServiceScope`; do not instantiate it directly. - Test both the selected interface path and, if supported, the concrete class path. - Add a deterministic signal in the custom interceptor and verify it occurs before/after the target once. - Verify exception behavior: the target exception still propagates and `finally` logic runs. - For a missing built-in behavior, confirm the module installed its registrar, the type matches the registrar trigger, and the method call crosses a proxy. ## Common Pitfalls - **A class method is not `virtual`** — class proxies cannot intercept it. Inject an exposed interface or make the method overridable. - **The object was created with `new`** — only the container-resolved proxy carries interceptors. - **The service was registered before the callback** — register selector callbacks in `PreConfigureServices`. - **The selector checks the service interface instead of `ImplementationType`** — `IOnServiceRegistredContext.ImplementationType` is what built-in registrars inspect. - **`ProceedAsync()` is called zero or multiple times** — zero short-circuits execution; multiple calls execute the downstream chain repeatedly. - **The type is deliberately ignored** — check `DynamicProxyIgnoreTypes`, `DisableAbpClassInterceptors`, and `DisableAbpFeaturesAttribute` before changing business code.
GitHub에서 보기