|
1 | 1 | # Middleware |
2 | 2 |
|
3 | | -Radar uses chain- or wrapper-style middleware for all _ServerRequest_ and |
4 | | -_Response_ processing. A middleware callable must have the following signature: |
| 3 | +Radar uses the [Pipeline](https://github.com/pipeline/Pipeline.Pipeline) system for dispatching middleware. You can read more about middleware there. |
5 | 4 |
|
6 | | -```php |
7 | | -use Psr\Http\Message\ResponseInterface; |
8 | | -use Psr\Http\Message\ServerRequestInterface; |
9 | | - |
10 | | -function ( |
11 | | - ServerRequestInterface $request, // the incoming request |
12 | | - ResponseInterface $response, // the outgoing response |
13 | | - callable $next // the next middleware handler |
14 | | -) { |
15 | | - // ... |
16 | | -} |
17 | | -``` |
18 | | - |
19 | | -> N.b.: There is no "route-specific" middleware in Radar. All middleware is |
20 | | -> called regardless of the routing and action results. See the "Middleware and |
21 | | -> Domain Activity" section below for the rationale behind this constraint. |
22 | | -
|
23 | | -## Invoking Middleware |
24 | | - |
25 | | -To add middleware logic to the execution path, call the `$adr->middle()` method |
26 | | -in `web/index.php`. Pass a class name as the only parameter to the method.The |
27 | | -underlying dependency injection container will create an instance of that class |
28 | | -and call its `__invoke()` method. |
| 5 | +To add Pipeline-compatible middleware logic to the execution path, call the |
| 6 | +`$adr->middle()` method in `web/index.php`. Pass a class name as the only |
| 7 | +parameter to the method.The underlying dependency injection container will |
| 8 | +create an instance of that class and call its `__invoke()` method. |
29 | 9 |
|
30 | 10 | ```php |
31 | 11 | $adr->middle('My\Middleware\Handler'); |
32 | 12 | ``` |
33 | 13 |
|
34 | | - Alternatively, pass an array of the form `['ClassName', 'method']`. In this |
| 14 | +Alternatively, pass an array of the form `['ClassName', 'method']`. In this |
35 | 15 | case, the underlying dependency injection container will create an instance of |
36 | 16 | that class and call the specified method. |
37 | 17 |
|
38 | | -## Middleware Logic |
39 | | - |
40 | | -Your middleware logic should follow this pattern: |
41 | | - |
42 | | -- Receive the incoming _ServerRequest_ and _Response_ objects from the previous |
43 | | - handler as parameters, along with the next handler as a callable. |
44 | | - |
45 | | -- Optionally modify the received _ServerRequest_ and _Response_ as desired. |
46 | | - |
47 | | -- Optionally invoke the next handler with the _ServerRequest_ and |
48 | | - _Response_, receiving a new _Response_ in return. |
49 | | - |
50 | | -- Optionally modify the returned _Response_ as desired. |
51 | | - |
52 | | -- Return the _Response_ to the previous handler. |
53 | | - |
54 | | -Here is a skeleton example; your own middleware may or may not perform the |
55 | | -various optional processes: |
56 | | - |
57 | | -```php |
58 | | -namespace My\Middleware; |
59 | | - |
60 | | -use Psr\Http\Message\ResponseInterface; |
61 | | -use Psr\Http\Message\ServerRequestInterface; |
62 | | - |
63 | | -class Handler |
64 | | -{ |
65 | | - public function __invoke( |
66 | | - ServerRequestInterface $request, |
67 | | - ResponseInterface $response, |
68 | | - callable $next |
69 | | - ) { |
70 | | - // optionally modify the incoming request |
71 | | - $request = $request->...; |
72 | | - |
73 | | - // optionally skip the $next handler and return early |
74 | | - if (...) { |
75 | | - return $response; |
76 | | - } |
77 | | - |
78 | | - // optionally invoke the $next handler and get back a new Response |
79 | | - $response = $next($request, $response); |
80 | | - |
81 | | - // optionally modify the Response if desired |
82 | | - $response = $response->...; |
83 | | - |
84 | | - // NOT OPTIONAL: return the Response to the previous handler |
85 | | - return $response; |
86 | | - } |
87 | | -} |
88 | | -``` |
89 | | - |
90 | | -> N.b.: You should **always** return the _Response_ from your middleware logic. |
91 | | -
|
92 | | -Remember that the _ServerRequest_ and _Response_ are **immutable**. Implicit in that is the fact that changes to the _ServerRequest_ are always transmitted to the `$next` handler but never to the |
93 | | -previous one. |
94 | | - |
95 | | -Note also that this logic chain means the _ServerRequest_ and _Response_ are |
96 | | -subjected to two passes through each middleware handler: |
97 | | - |
98 | | -- first on the way "in" through each handler via the `$next` handler invocation, |
99 | | -- then on the way "out" from each handler via the `return` to the previous handler. |
100 | | - |
101 | | -For example, if the middleware queue looks like this: |
102 | | - |
103 | | -``` |
104 | | -$adr->middle('FooHandler'); |
105 | | -$adr->middle('BarHandler'); |
106 | | -$adr->middle('BazHandler'); |
107 | | -``` |
108 | | - |
109 | | -... the _ServerRequest_ and _Response_ path through the handlers will look like |
110 | | -this: |
111 | | - |
112 | | -``` |
113 | | -FooHandler is 1st on the way in |
114 | | - BarHandler is 2nd on the way in |
115 | | - BazHandler is 3rd on the way in, and 1st on the way out |
116 | | - BarHandler is 2nd on the way out |
117 | | -FooHandler is 3rd on the way out |
118 | | -``` |
119 | | - |
120 | | -You can use this dual-pass logic in clever and perhaps unintuitive ways. For |
121 | | -example, a middlware handler placed at the very start may do nothing with |
122 | | -the _ServerRequest_ and call `$next` right away, but it is the handler with |
123 | | -the "real" last opportunity to modify the _Response_. |
124 | | - |
125 | 18 | ## Middleware Exceptions |
126 | 19 |
|
127 | | -If your middleware logic fails to catch an exception, the default |
| 20 | +If middleware in the pipeline fails to catch an exception, the default |
128 | 21 | _Radar\Adr\Handler\ExceptionHandler_ will catch it automatically. The |
129 | | -default _ExceptionHandler_ will: |
| 22 | +_ExceptionHandler_ will: |
130 | 23 |
|
131 | | -- append the _Exception_ message to the existing _Response_ body, |
| 24 | +- write the _Exception_ message to the _Response_ body, |
132 | 25 | - set a `500` HTTP status code, |
133 | 26 | - immediately send the _Response_ using the _Radar\Adr\Sender_, and |
134 | 27 | - return the sent _Response_ to the previous middleware handler. |
135 | 28 |
|
136 | | -This interrupts the execution of any `$next` middleware and starts the `return` |
137 | | -pass through the previous middleware handlers. |
138 | | - |
139 | | -You can set an exception handler of your own by calling |
140 | | -`$adr->exceptionHandler()` and passing a string class name, or an array of |
141 | | -string class name and string method name. |
142 | | - |
143 | | -``` |
144 | | -$adr->exceptionHandler('My\ExceptionHandler'); |
145 | | -``` |
146 | | - |
147 | | -The exception handler must match this signature: |
148 | | - |
149 | | -```php |
150 | | -use Exception; |
151 | | -use Psr\Http\Message\ResponseInterface; |
152 | | -use Psr\Http\Message\ServerRequestInterface; |
153 | | - |
154 | | -function ( |
155 | | - ServerRequestInterface $request, // the incoming request |
156 | | - ResponseInterface $response, // the outgoing response |
157 | | - Exception $exception // the exception |
158 | | -) { |
159 | | - // ... |
160 | | -} |
161 | | -``` |
| 29 | +The _ExceptionHandler_ should be the first middleware in the pipeline. |
162 | 30 |
|
163 | | -This is no opportunity to continue to a `$next` middleware handler. If doing so |
164 | | -is important to you, be diligent and catch exceptions yourself inside your |
165 | | -middleware logic. |
166 | 31 |
|
167 | 32 | ## Middleware And Domain Activity |
168 | 33 |
|
|
0 commit comments