5
5
[ ![ Unstable Version] ( https://img.shields.io/packagist/vpre/lcobucci/error-handling-middleware.svg?style=flat-square )] ( https://packagist.org/packages/lcobucci/error-handling-middleware )
6
6
7
7
![ Branch master] ( https://img.shields.io/badge/branch-master-brightgreen.svg?style=flat-square )
8
- [ ![ Build Status] ( https://img.shields.io/travis/lcobucci/error-handling-middleware/master.svg?style=flat-square )] ( http://travis-ci.org/#!/ lcobucci/error-handling-middleware )
8
+ [ ![ Build Status] ( https://img.shields.io/travis/lcobucci/error-handling-middleware/master.svg?style=flat-square )] ( http://travis-ci.org/lcobucci/error-handling-middleware )
9
9
[ ![ Scrutinizer Code Quality] ( https://img.shields.io/scrutinizer/g/lcobucci/error-handling-middleware/master.svg?style=flat-square )] ( https://scrutinizer-ci.com/g/lcobucci/error-handling-middleware/?branch=master )
10
10
[ ![ Code Coverage] ( https://img.shields.io/scrutinizer/coverage/g/lcobucci/error-handling-middleware/master.svg?style=flat-square )] ( https://scrutinizer-ci.com/g/lcobucci/error-handling-middleware/?branch=master )
11
11
@@ -22,16 +22,183 @@ flexibility to solve this problem.
22
22
23
23
## Installation
24
24
25
- This package is available on [ Packagist] ( http ://packagist.org/packages/lcobucci/error-handling-middleware) ,
26
- and we recommend you to install it using [ Composer] ( http ://getcomposer.org) :
25
+ This package is available on [ Packagist] ( https ://packagist.org/packages/lcobucci/error-handling-middleware) ,
26
+ and we recommend you to install it using [ Composer] ( https ://getcomposer.org) :
27
27
28
28
``` shell
29
29
composer require lcobucci/error-handling-middleware
30
30
```
31
31
32
32
## Usage
33
33
34
- TDB
34
+ In order to us this package you must add the middleware to your pipeline, configuring
35
+ the desired behaviour (debug info strategy and status code extraction strategy).
36
+
37
+ Once this is set you'll be able to have your errors/exceptions converted into the
38
+ correct HTTP responses.
39
+
40
+ ### Middleware position
41
+
42
+ This package provides two middleware for handling errors: error logging and error
43
+ conversion.
44
+
45
+ They are designed to be used in the very beginning of the HTTP middleware pipeline,
46
+ just after the [ content negotiation] ( https://github.com/lcobucci/content-negotiation-middleware ) one:
47
+
48
+ ``` php
49
+ <?php
50
+ use Lcobucci\ContentNegotiation\ContentTypeMiddleware;
51
+ use Lcobucci\ErrorHandling\ErrorConversionMiddleware;
52
+ use Lcobucci\ErrorHandling\ErrorLoggingMiddleware;
53
+
54
+ // In a Zend Expressive application, it would look like this:
55
+ $application->pipe(ContentTypeMiddleware::fromRecommendedSettings( /* ... */ )); // Very first middleware
56
+ $application->pipe(new ErrorConversionMiddleware( /* ... */ ));
57
+ $application->pipe(new ErrorLoggingMiddleware( /* ... */ ));
58
+
59
+ // all other middleware.
60
+ ```
61
+
62
+ With that we'll be able to perform the logging and conversion in the correct order,
63
+ delegating the content negotiation and formatting to ` ContentTypeMiddleware ` - using
64
+ the configured formatters.
65
+
66
+ #### Important
67
+
68
+ The ` ErrorConversionMiddleware ` uses an ` UnformattedResponse ` to let the
69
+ ` ContentTypeMiddleware ` perform the formatting. Make sure you have configured
70
+ formatters for the MIME types ` application/problem+json ` and/or
71
+ ` application/problem+xml ` .
72
+
73
+ It also makes the error/exception available in the ` error ` attribute of the response,
74
+ so you may access it (if needed) by using another middleware between
75
+ ` ErrorConversionMiddleware ` and ` ContentTypeMiddleware ` .
76
+
77
+ ### Configuring the conversion middleware behaviour
78
+
79
+ There're two extension points that you can use for that: debug info strategy and
80
+ status code extraction strategy.
81
+
82
+ You can also configure the response body attributes by implementing certain interfaces
83
+ in your exceptions.
84
+
85
+ #### Debug info strategy
86
+
87
+ This defines how the ` _debug ` property should be generated in the response body.
88
+ We provide two default implementations - one designed for production mode and the
89
+ other for development mode.
90
+
91
+ To configure this you must pass the desired implementation (or a customised one) as
92
+ the second argument of the ` ErrorConversionMiddleware ` constructor.
93
+
94
+ To provide your own implementation you need to create a class that implements the
95
+ ` DebugInfoStrategy ` interface.
96
+
97
+ #### Status code extraction strategy
98
+
99
+ This defines how the translation from error/exception to HTTP status code should
100
+ be done. We provide a single default implementation for that, which is based on
101
+ class maps.
102
+
103
+ To configure this you must pass the desired implementation (or a customised one) as
104
+ the third argument of the ` ErrorConversionMiddleware ` constructor.
105
+
106
+ To provide your own implementation you need to create a class that implements the
107
+ ` StatusCodeExtractionStrategy ` interface.
108
+
109
+ ##### Default class map
110
+
111
+ The default map uses the marker interfaces in this packages to perform such translation.
112
+ If the error/exception doesn't implement any of the marker interfaces, the error/exception
113
+ code will be used (when it's different than zero), or fallback to the status code
114
+ 500 (Internal Server Error).
115
+
116
+ The default map is:
117
+
118
+ * ` Lcobucci\ErrorHandling\Problem\InvalidRequest ` -> ` 400 `
119
+ * ` Lcobucci\ErrorHandling\Problem\AuthorizationRequired ` -> ` 401 `
120
+ * ` Lcobucci\ErrorHandling\Problem\Forbidden ` -> ` 403 `
121
+ * ` Lcobucci\ErrorHandling\Problem\ResourceNotFound ` -> ` 404 `
122
+ * ` Lcobucci\ErrorHandling\Problem\Conflict ` -> ` 409 `
123
+ * ` Lcobucci\ErrorHandling\Problem\ResourceNoLongerAvailable ` -> ` 410 `
124
+ * ` Lcobucci\ErrorHandling\Problem\UnprocessableRequest ` -> ` 422 `
125
+ * ` Lcobucci\ErrorHandling\Problem\ServiceUnavailable ` -> ` 503 `
126
+
127
+ This allows us to create our own exceptions that are automatically converted to the
128
+ correct status code:
129
+
130
+ ``` php
131
+ <?php
132
+ declare(strict_types=1);
133
+
134
+ namespace My\Fancy\App\UserManagement;
135
+
136
+ use Lcobucci\ErrorHandling\Problem\ResourceNotFound;
137
+ use RuntimeException;
138
+
139
+ final class UserNotFound extends RuntimeException implements ResourceNotFound
140
+ {
141
+ }
142
+ ```
143
+
144
+ ** Important** : you SHOULD NOT implement more than one of the marker interfaces,
145
+ otherwise you may have unexpected results.
146
+
147
+ #### Customising the response body properties
148
+
149
+ With this library, you may modify the ` type ` and ` title ` properties of the generated
150
+ response and also append new members to it.
151
+
152
+ That's done by implementing the ` Typed ` , ` Titled ` , and/or ` Detailed ` interfaces -
153
+ you don't necessarily need to implement all of them, only the ones you want.
154
+
155
+ The example below shows how to represent one of the samples in the
156
+ [ RFC 7807] ( https://tools.ietf.org/html/rfc7807#section-3 ) :
157
+
158
+ ``` php
159
+ <?php
160
+ declare(strict_types=1);
161
+
162
+ namespace My\Fancy\App\UserManagement;
163
+
164
+ use Lcobucci\ErrorHandling\Problem\Forbidden;
165
+ use Lcobucci\ErrorHandling\Problem\Typed;
166
+ use Lcobucci\ErrorHandling\Problem\Titled;
167
+ use Lcobucci\ErrorHandling\Problem\Detailed;
168
+ use RuntimeException;
169
+ use function sprintf;
170
+
171
+ final class InsufficientCredit extends RuntimeException implements Forbidden, Typed, Titled, Detailed
172
+ {
173
+ private $currentBalance;
174
+
175
+ public static function forPurchase(int $currentBalance, int $cost): self
176
+ {
177
+ $exception = new self(sprintf('Your current balance is %d, but that costs %d.', $currentBalance, $cost));
178
+ $exception->currentBalance = $currentBalance;
179
+
180
+ return $exception;
181
+ }
182
+
183
+ public function getTypeUri(): string
184
+ {
185
+ return 'https://example.com/probs/out-of-credit';
186
+ }
187
+
188
+ public function getTitle(): string
189
+ {
190
+ return 'You do not have enough credit.';
191
+ }
192
+
193
+ /**
194
+ * {@inheritDoc}
195
+ */
196
+ public function getExtraDetails(): array
197
+ {
198
+ return ['balance' => $this->currentBalance]; // you might add "instance" and "accounts" too :)
199
+ }
200
+ }
201
+ ```
35
202
36
203
## License
37
204
0 commit comments