# Middleware
The EasyWebhook package uses a middleware stack that is processed every time a webhook is sent. Processing of the middleware proceeds down the stack to where the webhook is sent in a HTTP request, and then proceeds back up the stack. Actions may be taken on the way down and/or the way back up.
Middleware are services implementing EonX\EasyWebhook\Interfaces\MiddlewareInterface
. This package provides core
middleware, but you can also write custom middleware, e.g. to add a custom header to webhook HTTP requests.
The middleware is ordered by priority in the stack, whereby the lower the priority, the earlier in the stack the middleware is placed.
Custom middleware can have priorities between -5000 and 5000. If not specified, the default priority is 0. Some core middleware provided by this package must run before the custom middleware, e.g. to lock the Webhook object, and some core middleware must run after the core middleware, e.g. to send the webhook HTTP request.
# Configure once middleware
Some middleware are configure once middleware. If the webhook is flagged as configured, i.e. its $configured
property is true
, then configure once middleware will not operate on the webhook.
Note that if a webhook is retrieved from a webhook store, then it is automatically flagged as configured.
# Middleware list
The following middleware are provided by the EasyWebhook package:
# AsyncMiddleware
This middleware checks if the webhook should be sent synchronously. If so, this middleware proceeds with the remaining middleware in the stack. If the webhook should be sent asynchronously, then this middleware stores the webhook and offloads dispatching of the webhook to a framework-specific dispatcher.
Note that the dispatcher will retrieve the webhook from the store and trigger the processing of the middleware stack again. The webhook is flagged as configured due to being retrieved from the store, so configure once middleware will not operate on the webhook.
# BodyFormatterMiddleware
This middleware formats the body of the webhook HTTP request.
By default, this package uses the EonX\EasyWebhook\Formatters\JsonFormatter
body formatter, which formats the request
body as a JSON string and sets the Content-Type
header of the HTTP request to be application/json
.
You can use your own body formatter by setting the EonX\EasyWebhook\Interfaces\WebhookBodyFormatterInterface
service
to your own implementation.
This is configure once middleware.
# EventHeaderMiddleware
This middleware sets the Event header of the webhook HTTP request to the webhook's $event
property (if it exists).
The default name of the Event header is X-Webhook-Event
, but the name is configurable (see
Configuration).
This is configure once middleware.
# EventsMiddleware
This middleware dispatches an event depending on the outcome of the webhook HTTP request. See see Events) for more information.
# HandleExceptionsMiddleware
This middleware catches exceptions that are thrown within the stack and handles them gracefully by returning a failed WebhookResult containing the actual exception.
TIP
To prevent an exception to be handled by this middleware, simply implement EonX\EasyWebhook\Interfaces\DoNotHandleMeEasyWebhookExceptionInterface
.
# IdHeaderMiddleware
This middleware sets the ID header of the webhook HTTP request to the webhook's $id
property (if it exists) or
generates a new ID.
The default name of the ID header is X-Webhook-Id
, but the name is configurable (see Configuration).
This is configure once middleware.
# LockMiddleware
This middleware locks the Webhook object at the start of middleware processing and unlocks it at the end.
This middleware prevents the same webhook from being sent more than once if there are concurrency issues with multiple workers consuming the asynchronous webhook queue.
# MethodMiddleware
This middleware sets the HTTP method for sending the webhook HTTP request to the webhook's $method
property (if it
exists) or the method defined in the package configuration (see Configuration). The default method is
POST
.
This is configure once middleware.
# RerunMiddleware
If the webhook is in a final state (i.e. success or failed), this middleware checks if rerun is allowed for the webhook,
and if so, resets its $currentAttempt
and its $status
to pending
, which allows the webhook to be rerun.
This middleware ensures that webhooks will not be rerun after reaching a final state unless explicitly allowed by the webhook.
# ResetStoreMiddleware
This middleware resets the webhook store and webhook results store. See Stores for more information.
This middleware prevents memory issues when sending webhooks asynchronously with stores that use memory for their storage, such as the array stores.
Note that the stores must implement EonX\EasyWebhook\Interfaces\Stores\ResetStoreInterface
. Of the stores provided by
the EasyWebhook package, only the array stores support reset.
# SendAfterMiddleware
This middleware checks the $sendAfter
property of the webhook, and if it exists and is after the current date and
time, it simply stores the webhook in the configured store, and does not proceed with any further middleware processing.
You can initiate sending of delayed webhooks via the console command easy-webhooks:send-due-webhooks
. See
Console for more information.
Note that the webhook store must implement EonX\EasyWebhook\Interfaces\Stores\SendAfterStoreInterface
in order to be
able to find due webhooks. Of the stores provided by the EasyWebhook package, only the Doctrine DBAL webhook store
supports finding due webhooks.
# SendWebhookMiddleware
This middleware sends the webhook HTTP request and returns the result in a WebhookResult object.
# SignatureHeaderMiddleware
This middleware sets the Signature header of the webhook HTTP request.
By default, this package uses the EonX\EasyWebhook\Signers\Rs256Signer
signer. The header will contain a SHA-256 HMAC
(Hash-based Message Authentication Code) constructed by hashing the webhook's request body with either the webhook's
$secret
property (if it exists) or the secret defined in the package configuration (see Configuration).
You can use your own signer by setting the EonX\EasyWebhook\Interfaces\WebhookSignerInterface
service to your own
implementation.
The default name of the Signature header is X-Webhook-Signature
, but the name is configurable (see
Configuration).
This is configure once middleware.
# StatusAndAttemptMiddleware
This middleware updates the webhook status and current attempt after a response has been received from the webhook HTTP request.
The current attempt is incremented.
The status can be set to one of:
success
: The webhook was successfully sent.failed
: The webhook sending failed and the webhook has reached the maximum number of attempts allowedfailed_pending_retry
: The webhook sending failed but the webhook has not reached the maximum number of attempts allowed
# StoreMiddleware
This middleware stores the webhook and webhook result in the configured stores after a response has been received from the webhook HTTP request. See Stores for more information.
# SyncRetryMiddleware
If the webhook was sent synchronously, and it failed, this middleware retries sending the webhook. It provides a simple out-of-the-box solution for handling retries. However, we strongly recommend sending webhooks asynchronously, so your application is not blocked by retries.
# Middleware stack
The following table show the middleware stack in priority order, with summaries of their actions:
Middleware | Action forward ⬇️ | Action back ⬆️ |
---|---|---|
Begin initial core middleware | ||
LockMiddleware | Lock webhook | Unlock webhook |
StoreMiddleware | Store webhook and result | |
StatusAndAttemptMiddleware | Update status and attempt | |
HandleExceptionsMiddleware | Handle exception thrown within the stack | |
ResetStoreMiddleware | Reset webhook and result stores | |
EventsMiddleware | Dispatch event | |
RerunMiddleware | If rerun allowed, reset status and current attempt If not allowed, throw exception | |
End initial core middleware | ||
Begin custom middleware (processed in priority order) | ||
BodyFormatterMiddleware | Format request body | |
EventHeaderMiddleware | Set Event request header | |
IdHeaderMiddleware | Set ID request header | |
SignatureHeaderMiddleware | Set Signature request header | |
Custom middleware | Custom pre-processing | Custom post-processing |
End custom middleware | ||
Begin final core middleware | ||
MethodMiddleware | Set request method | |
SendAfterMiddleware | If time is before $sendAfter , store webhook and return up stackIf time is after $sendAfter , continue down stack | |
AsyncMiddleware | If asynchronous, store webhook and return up stack If synchronous, continue down stack | |
SyncRetryMiddleware | If asynchronous, continue down stack If synchronous, retries webhook if not successful | |
SendWebhookMiddleware | Send request and return up stack | |
End final core middleware |
# Custom middleware
Custom middleware must implement EonX\EasyWebhook\Interfaces\MiddlewareInterface
. However, it may be easier to extend
one of the following classes:
EonX\EasyWebhook\Middleware\AbstractConfigureOnceMiddleware
: For middleware that should not be processed if the webhook is already "configured". Implement thedoProcess()
method.EonX\EasyWebhook\Middleware\AbstractMiddleware
: For middleware that should be processed regardless of whether the webhook is "configured". Implement theprocess()
method.
For example, here is how you would add a custom header to every webhook sent in configure once middleware:
use EonX\EasyWebhook\Interfaces\StackInterface;
use EonX\EasyWebhook\Interfaces\WebhookInterface;
use EonX\EasyWebhook\Interfaces\WebhookResultInterface;
final class MyCustomHeaderMiddleware extends AbstractConfigureOnceMiddleware
{
protected function doProcess(WebhookInterface $webhook, StackInterface $stack): WebhookResultInterface
{
/* Add custom header */
$webhook->header('X-My-Header', 'my-value');
/* Send webhook down stack */
return $stack
->next()
->process($webhook, $stack);
}
}
This example shows placement of custom pre-processing and post-processing in normal middleware:
use EonX\EasyWebhook\Interfaces\StackInterface;
use EonX\EasyWebhook\Interfaces\WebhookInterface;
use EonX\EasyWebhook\Interfaces\WebhookResultInterface;
final class MyCustomMiddleware extends AbstractMiddleware
{
public function process(WebhookInterface $webhook, StackInterface $stack): WebhookResultInterface
{
/* Add custom pre-processing */
/* Send webhook down stack */
$webhookResult = $stack
->next()
->process($webhook, $stack);
/* Add custom post-processing */
/* Return webhook result up stack */
return $webhookResult;
}
}