Interceptors (Middlewares)
PHP Interceptors Middlewares
Ecotone
provide possibility to handle cross cutting concerns via Interceptors
.
Interceptor
intercepts the process of handling the message, this means we can do actions like:
Enriching the message
Stopping or modify usual processing cycle
Calling some shared functionality or adding additional behavior
This all can be done without modifying the code itself, as we hook into the existing flows.
If you are familiar with Aspect Oriented Programming you will find a lot of similarities.
Interceptor
Before Attribute
Type of Interceptor more about it Interceptor Types section
Precedence
Precedence defines ordering of called interceptors. The lower the value is, the quicker Interceptor will be called. It's safe to stay with range between -1000 and 1000, as numbers bellow -1000 and higher than 1000 are used by Ecotone.
The precedence is done within a specific interceptor type.
Pointcut
Every interceptor has Pointcut
attribute, which describes for specific interceptor, which endpoints it should intercept.
CLASS_NAME
- indicates intercepting specific class or interface or class containing attribute on method or class levelCLASS_NAME::METHOD_NAME
- indicates intercepting specific method of classNAMESPACE*
- Indicating all Endpoints starting with namespace prefix e.g.App\Domain\*
expression || expression
- Indicating one expression or another e.g.Product\*||Order\*
expression && expression
- Indicating one expression and another e.g.App\Domain\* && App\Attribute\RequireAdministrator
Interceptor Types
There are four types of interceptors. Each interceptor has it role and possibilities. Interceptors are called in following order:
Before
Around
After
Presend
Before Interceptor
Before Interceptor is called after message is sent to the channel, before execution of Endpoint.
- Exceptional Interceptor
Before interceptor is called before endpoint is executed.
Before interceptors can used in order to stop the flow
, throw an exception
or enrich the
Message.
To understand it better, let's follow an example, where we will intercept Command Handler with verification if executor is an administrator.
Let's start by creating Attribute called RequireAdministrator in new namepace.
Let's create our first Before Interceptor.
We are using in here Pointcut which is looking for #[RequireAdministrator] annotation in each of registered Endpoints.
The void return type
is expected in here. It tells Ecotone
that, this Before Interceptor is not modifying the Message and message will be passed through. The message flow however can be interrupted by throwing exception.
Now we need to annotate our Command Handler:
Whenever we call our command handler, it will be intercepted by AdminVerificator now.
Our Command Handler
is using ChangePriceCommand
class and our AdminVerificator interceptor
is using array $payload
. They are both referencing payload of the Message, yet if we define a class as type hint, Ecotone will do the Conversion for us.
- Payload Enriching Interceptor
If return type is not void
new Message will be created from the returned type.
Let's follow an example, where we will enrich Message payload with timestamp.
- Header Enriching Interceptor
Suppose we want to add executor Id, but as this is not part of our Command, we want add it to our Message Headers.
If return type is not void
new modified based on previous Message will be created from the returned type. If we additionally add changeHeaders: true it will tell Ecotone, that we we want to modify Message headers instead of payload.
- Message Filter Interceptor
Use Message Filter
, to eliminate undesired messages based on a set of criteria.
This can be done by returning null from interceptor, if the flow should proceed, then payload should be returned.
If return type is not void
new modified based on previous Message will be created from the returned type. If we additionally add changeHeaders=true
it will tell Ecotone, that we we want to modify Message headers instead of payload.
Around Interceptor
The Around Interceptor
have access to actual Method Invocation.
This does allow for starting action before method invocation is done, and finishing it after.
Around interceptor
is a good place for handling actions like Database Transactions or logic that need to access invoked object.
As we used Command Bus
interface as pointcut, we told Ecotone
that it should intercept Command Bus Gateway.
Now whenever we will call any method on Command Bus, it will be intercepted with transaction.
The other powerful use case for Around Interceptor is intercepting Aggregate.
Suppose we want to verify, if executing user has access to the Aggregate.
We have placed @IsOwnerOfPerson
annotation as the top of class. For interceptor pointcut it means, that each endpoint defined in this class should be intercepted. No need to add it on each Command Handler now.
We've passed the executd Aggregate instance - Person to our Interceptor. This way we can get the context of the executed object in order to perform specific logic.
After Interceptor
After interceptor
is called after endpoint execution has finished.
It does work exactly the same as Before Interceptor.
After interceptor can used to for example to enrich QueryHandler
result.
We will intercept all endpoints within Order\ReadModel namespace, by adding result coming from the endpoint under result
key.
Access attribute from interceptor
We may access attribute from the intercepted endpoint in order to perform specific action
If you type hint for specific attribute, you do not need to add pointcut, as it will resolve it automatically.
Presend Interceptor
Presend Interceptor
is called before Message is actually send to the channel.
In synchronous channel there is no difference between Before
and Presend.
The difference is seen when the channel is asynchronous.
Presend Interceptor
can used to verify if data is correct before sending to asynchronous channel, or we may want to check if user has enough permissions to do given action.
This will keep our asynchronous channel free of incorrect messages.
Last updated