Back to Abp

Interceptors

docs/en/low-code/interceptors.md

10.3.08.2 KB
Original Source
json
//[doc-seo]
{
    "Description": "Add custom business logic to dynamic entity CRUD operations using Interceptors in the ABP Low-Code System. Validate, transform, and react to data changes with JavaScript."
}

Interceptors

Interceptors allow you to run custom JavaScript code before, after, or instead of Create, Update, and Delete operations on dynamic entities.

Interceptor Types

CommandTypeWhen Executed
CreatePreBefore entity creation — validation, default values
CreatePostAfter entity creation — notifications, related data
CreateReplaceInstead of entity creation — must return the new entity's Id (see below)
UpdatePreBefore entity update — validation, authorization
UpdatePostAfter entity update — sync, notifications
UpdateReplaceInstead of entity update — no return value needed
DeletePreBefore entity deletion — dependency checks
DeletePostAfter entity deletion — cleanup
DeleteReplaceInstead of entity deletion — no return value needed

Defining Interceptors with Attributes

Use the [DynamicEntityCommandInterceptor] attribute on a C# class:

csharp
[DynamicEntity]
[DynamicEntityCommandInterceptor(
    "Create",
    InterceptorType.Pre,
    "if(!context.commandArgs.data['Name']) { globalError = 'Name is required!'; }"
)]
[DynamicEntityCommandInterceptor(
    "Create",
    InterceptorType.Post,
    "context.log('Entity created: ' + context.commandArgs.entityId);"
)]
public class Organization
{
    public string Name { get; set; }
}

The Name parameter must be one of: "Create", "Update", or "Delete". The InterceptorType can be Pre, Post, or Replace. When Replace is used, the default database operation is completely skipped and only your JavaScript handler executes. Multiple interceptors can be added to the same class (AllowMultiple = true).

Defining Interceptors with Fluent API

Use the Interceptors list on an EntityDescriptor to add interceptors programmatically in your Low-Code Initializer:

csharp
AbpDynamicEntityConfig.EntityConfigurations.Configure(
    "MyApp.Organizations.Organization",
    entity =>
    {
        entity.Interceptors.Add(new CommandInterceptorDescriptor("Create")
        {
            Type = InterceptorType.Pre,
            Javascript = "if(!context.commandArgs.data['Name']) { globalError = 'Name is required!'; }"
        });

        entity.Interceptors.Add(new CommandInterceptorDescriptor("Delete")
        {
            Type = InterceptorType.Post,
            Javascript = "context.log('Deleted: ' + context.commandArgs.entityId);"
        });
    }
);

See Attributes & Fluent API for more details on Fluent API configuration.

Defining Interceptors in model.json

Add interceptors to the interceptors array of an entity:

json
{
  "name": "LowCodeDemo.Customers.Customer",
  "interceptors": [
    {
      "commandName": "Create",
      "type": "Pre",
      "javascript": "if(context.commandArgs.data['Name'] == 'Invalid') {\n  globalError = 'Invalid Customer Name!';\n}"
    }
  ]
}

Interceptor Descriptor

FieldTypeDescription
commandNamestring"Create", "Update", or "Delete"
typestring"Pre", "Post", or "Replace"
javascriptstringJavaScript code to execute

JavaScript Context

Inside interceptor scripts, you have access to:

context.commandArgs

Property / MethodTypeDescription
dataobjectEntity data dictionary (for Create/Update)
entityIdstringEntity ID (for Update/Delete)
commandNamestringCommand name ("Create", "Update", or "Delete")
entityNamestringFull entity name
getValue(name)functionGet a property value
setValue(name, value)functionSet a property value (Pre-interceptors only)
hasValue(name)functionCheck if a property exists in the data
removeValue(name)functionRemove a property from the data

context.currentUser

Property / MethodTypeDescription
isAuthenticatedboolWhether user is logged in
idstringUser ID
userNamestringUsername
emailstringEmail address
namestringFirst name
surNamestringLast name
phoneNumberstringPhone number
phoneNumberVerifiedboolWhether phone is verified
emailVerifiedboolWhether email is verified
tenantIdstringTenant ID (for multi-tenant apps)
rolesstring[]User's role names
isInRole(roleName)functionCheck if user has a specific role

context.emailSender

Property / MethodDescription
isAvailableWhether the email sender is configured and available
sendAsync(to, subject, body)Send a plain-text email
sendHtmlAsync(to, subject, htmlBody)Send an HTML email

Logging

MethodDescription
context.log(message)Log an informational message
context.logWarning(message)Log a warning message
context.logError(message)Log an error message

Use these methods instead of console.log (which is blocked in the sandbox).

db (Database API)

Full access to the Scripting API for querying and mutating data.

globalError

Set this variable to a string to abort the operation and return an error:

javascript
globalError = 'Cannot delete this entity!';

Examples

Pre-Create: Validation

json
{
  "commandName": "Create",
  "type": "Pre",
  "javascript": "if(!context.commandArgs.data['Name']) {\n  globalError = 'Organization name is required!';\n}"
}

Post-Create: Email Notification

json
{
  "commandName": "Create",
  "type": "Post",
  "javascript": "if(context.currentUser.isAuthenticated && context.emailSender) {\n  await context.emailSender.sendAsync(\n    context.currentUser.email,\n    'New Order Created',\n    'Order total: $' + context.commandArgs.data['TotalAmount']\n  );\n}"
}

Pre-Update: Role-Based Authorization

json
{
  "commandName": "Update",
  "type": "Pre",
  "javascript": "if(context.commandArgs.data['IsDelivered']) {\n  if(!context.currentUser.roles.includes('admin')) {\n    globalError = 'Only administrators can mark orders as delivered!';\n  }\n}"
}

Pre-Delete: Business Rule Check

json
{
  "commandName": "Delete",
  "type": "Pre",
  "javascript": "var project = await db.get('LowCodeDemo.Projects.Project', context.commandArgs.entityId);\nif(project.Budget > 100000) {\n  globalError = 'Cannot delete high-budget projects!';\n}"
}

Pre-Update: Negative Value Check

json
{
  "commandName": "Update",
  "type": "Pre",
  "javascript": "if(context.commandArgs.data['Quantity'] < 0) {\n  globalError = 'Quantity cannot be negative!';\n}"
}

Replace-Create: Custom Insert Logic

When you need to completely replace the default create operation with custom logic:

json
{
  "commandName": "Create",
  "type": "Replace",
  "javascript": "var data = context.commandArgs.data;\ndata['Code'] = 'PRD-' + Date.now();\nvar result = await db.insert('LowCodeDemo.Products.Product', data);\ncontext.log('Product created with custom code: ' + data['Code']);\nreturn result.Id;"
}

Important: Replace-Create interceptors must return the new entity's Id (Guid). The system uses this value to fetch and return the created entity. Use return result.Id; after db.insert(...).

Replace-Update and Replace-Delete interceptors do not need to return a value.

Pre-Update: Self-Reference Check

json
{
  "commandName": "Update",
  "type": "Pre",
  "javascript": "if(context.commandArgs.data.ParentCategoryId === context.commandArgs.entityId) {\n  globalError = 'A category cannot be its own parent!';\n}"
}

See Also