diff --git a/README.md b/README.md index 44abdfe..1bf57e5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Unstable Version](https://img.shields.io/packagist/vpre/lcobucci/error-handling-middleware.svg?style=flat-square)](https://packagist.org/packages/lcobucci/error-handling-middleware) ![Branch master](https://img.shields.io/badge/branch-master-brightgreen.svg?style=flat-square) -[![Build Status](https://img.shields.io/travis/lcobucci/error-handling-middleware/master.svg?style=flat-square)](http://travis-ci.org/#!/lcobucci/error-handling-middleware) +[![Build Status](https://img.shields.io/travis/lcobucci/error-handling-middleware/master.svg?style=flat-square)](http://travis-ci.org/lcobucci/error-handling-middleware) [![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) [![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) @@ -22,8 +22,8 @@ flexibility to solve this problem. ## Installation -This package is available on [Packagist](http://packagist.org/packages/lcobucci/error-handling-middleware), -and we recommend you to install it using [Composer](http://getcomposer.org): +This package is available on [Packagist](https://packagist.org/packages/lcobucci/error-handling-middleware), +and we recommend you to install it using [Composer](https://getcomposer.org): ```shell composer require lcobucci/error-handling-middleware @@ -31,7 +31,174 @@ composer require lcobucci/error-handling-middleware ## Usage -TDB +In order to us this package you must add the middleware to your pipeline, configuring +the desired behaviour (debug info strategy and status code extraction strategy). + +Once this is set you'll be able to have your errors/exceptions converted into the +correct HTTP responses. + +### Middleware position + +This package provides two middleware for handling errors: error logging and error +conversion. + +They are designed to be used in the very beginning of the HTTP middleware pipeline, +just after the [content negotiation](https://github.com/lcobucci/content-negotiation-middleware) one: + +```php +pipe(ContentTypeMiddleware::fromRecommendedSettings( /* ... */ )); // Very first middleware +$application->pipe(new ErrorConversionMiddleware( /* ... */ )); +$application->pipe(new ErrorLoggingMiddleware( /* ... */ )); + +// all other middleware. +``` + +With that we'll be able to perform the logging and conversion in the correct order, +delegating the content negotiation and formatting to `ContentTypeMiddleware` - using +the configured formatters. + +#### Important + +The `ErrorConversionMiddleware` uses an `UnformattedResponse` to let the +`ContentTypeMiddleware` perform the formatting. Make sure you have configured +formatters for the MIME types `application/problem+json` and/or +`application/problem+xml`. + +It also makes the error/exception available in the `error` attribute of the response, +so you may access it (if needed) by using another middleware between +`ErrorConversionMiddleware` and `ContentTypeMiddleware`. + +### Configuring the conversion middleware behaviour + +There're two extension points that you can use for that: debug info strategy and +status code extraction strategy. + +You can also configure the response body attributes by implementing certain interfaces +in your exceptions. + +#### Debug info strategy + +This defines how the `_debug` property should be generated in the response body. +We provide two default implementations - one designed for production mode and the +other for development mode. + +To configure this you must pass the desired implementation (or a customised one) as +the second argument of the `ErrorConversionMiddleware` constructor. + +To provide your own implementation you need to create a class that implements the +`DebugInfoStrategy` interface. + +#### Status code extraction strategy + +This defines how the translation from error/exception to HTTP status code should +be done. We provide a single default implementation for that, which is based on +class maps. + +To configure this you must pass the desired implementation (or a customised one) as +the third argument of the `ErrorConversionMiddleware` constructor. + +To provide your own implementation you need to create a class that implements the +`StatusCodeExtractionStrategy` interface. + +##### Default class map + +The default map uses the marker interfaces in this packages to perform such translation. +If the error/exception doesn't implement any of the marker interfaces, the error/exception +code will be used (when it's different than zero), or fallback to the status code +500 (Internal Server Error). + +The default map is: + +* `Lcobucci\ErrorHandling\Problem\InvalidRequest` -> `400` +* `Lcobucci\ErrorHandling\Problem\AuthorizationRequired` -> `401` +* `Lcobucci\ErrorHandling\Problem\Forbidden` -> `403` +* `Lcobucci\ErrorHandling\Problem\ResourceNotFound` -> `404` +* `Lcobucci\ErrorHandling\Problem\Conflict` -> `409` +* `Lcobucci\ErrorHandling\Problem\ResourceNoLongerAvailable` -> `410` +* `Lcobucci\ErrorHandling\Problem\UnprocessableRequest` -> `422` +* `Lcobucci\ErrorHandling\Problem\ServiceUnavailable`-> `503` + +This allows us to create our own exceptions that are automatically converted to the +correct status code: + +```php +currentBalance = $currentBalance; + + return $exception; + } + + public function getTypeUri(): string + { + return 'https://example.com/probs/out-of-credit'; + } + + public function getTitle(): string + { + return 'You do not have enough credit.'; + } + + /** + * {@inheritDoc} + */ + public function getExtraDetails(): array + { + return ['balance' => $this->currentBalance]; // you might add "instance" and "accounts" too :) + } +} +``` ## License diff --git a/src/DebugInfoStrategy.php b/src/DebugInfoStrategy.php index 4bfbbe2..a44f919 100644 --- a/src/DebugInfoStrategy.php +++ b/src/DebugInfoStrategy.php @@ -5,6 +5,9 @@ use Throwable; +/** + * Defines how the debug information should be extracted from an error/exception + */ interface DebugInfoStrategy { /** diff --git a/src/Problem/AuthorizationRequired.php b/src/Problem/AuthorizationRequired.php index a5179cd..521f9d9 100644 --- a/src/Problem/AuthorizationRequired.php +++ b/src/Problem/AuthorizationRequired.php @@ -5,6 +5,9 @@ use Throwable; +/** + * Marker interface to be used in exceptions related to missing auth information (credentials or valid access token) + */ interface AuthorizationRequired extends Throwable { } diff --git a/src/Problem/Conflict.php b/src/Problem/Conflict.php index 2c27bfa..557c803 100644 --- a/src/Problem/Conflict.php +++ b/src/Problem/Conflict.php @@ -5,6 +5,9 @@ use Throwable; +/** + * Marker interface to be used in exceptions related to resource conflicts (version mismatch or duplicated data) + */ interface Conflict extends Throwable { } diff --git a/src/Problem/Detailed.php b/src/Problem/Detailed.php index 7f3c155..a416092 100644 --- a/src/Problem/Detailed.php +++ b/src/Problem/Detailed.php @@ -5,6 +5,11 @@ use Throwable; +/** + * Provides extension members to the problem details + * + * @see https://tools.ietf.org/html/rfc7807#section-3.2 + */ interface Detailed extends Throwable { /** diff --git a/src/Problem/Forbidden.php b/src/Problem/Forbidden.php index 879f8d8..b8abfeb 100644 --- a/src/Problem/Forbidden.php +++ b/src/Problem/Forbidden.php @@ -5,6 +5,9 @@ use Throwable; +/** + * Marker interface to be used in exceptions related to forbidden operations (lack of permission) + */ interface Forbidden extends Throwable { } diff --git a/src/Problem/InvalidRequest.php b/src/Problem/InvalidRequest.php index db8f339..47e3533 100644 --- a/src/Problem/InvalidRequest.php +++ b/src/Problem/InvalidRequest.php @@ -5,6 +5,9 @@ use Throwable; +/** + * Marker interface to be used in exceptions related to malformed requests (syntax issues) + */ interface InvalidRequest extends Throwable { } diff --git a/src/Problem/ResourceNoLongerAvailable.php b/src/Problem/ResourceNoLongerAvailable.php index e2254b8..1756c11 100644 --- a/src/Problem/ResourceNoLongerAvailable.php +++ b/src/Problem/ResourceNoLongerAvailable.php @@ -5,6 +5,9 @@ use Throwable; +/** + * Marker interface to be used in exceptions related to removed resource (removed/archived items) + */ interface ResourceNoLongerAvailable extends Throwable { } diff --git a/src/Problem/ResourceNotFound.php b/src/Problem/ResourceNotFound.php index 3d2e432..328fb81 100644 --- a/src/Problem/ResourceNotFound.php +++ b/src/Problem/ResourceNotFound.php @@ -5,6 +5,9 @@ use Throwable; +/** + * Marker interface to be used in exceptions related to non-existing resources + */ interface ResourceNotFound extends Throwable { } diff --git a/src/Problem/ServiceUnavailable.php b/src/Problem/ServiceUnavailable.php index 957d3eb..8821be7 100644 --- a/src/Problem/ServiceUnavailable.php +++ b/src/Problem/ServiceUnavailable.php @@ -5,6 +5,9 @@ use Throwable; +/** + * Marker interface to be used in exceptions related to availability issues (maintenance or dependency issues) + */ interface ServiceUnavailable extends Throwable { } diff --git a/src/Problem/Titled.php b/src/Problem/Titled.php index fc5dfd7..66ceb86 100644 --- a/src/Problem/Titled.php +++ b/src/Problem/Titled.php @@ -5,6 +5,13 @@ use Throwable; +/** + * Provides a custom (human-readable) summary of the problem + * + * The provided title SHOULD NOT vary from occurrence to occurrence. + * + * @see https://tools.ietf.org/html/rfc7807#section-3.1 + */ interface Titled extends Throwable { public function getTitle(): string; diff --git a/src/Problem/Typed.php b/src/Problem/Typed.php index 080debc..85ad8ca 100644 --- a/src/Problem/Typed.php +++ b/src/Problem/Typed.php @@ -5,6 +5,11 @@ use Throwable; +/** + * Provides a custom URI for the documentation of the problem + * + * @see https://tools.ietf.org/html/rfc7807#section-3.1 + */ interface Typed extends Throwable { public function getTypeUri(): string; diff --git a/src/Problem/UnprocessableRequest.php b/src/Problem/UnprocessableRequest.php index c39f333..1c957d0 100644 --- a/src/Problem/UnprocessableRequest.php +++ b/src/Problem/UnprocessableRequest.php @@ -5,6 +5,9 @@ use Throwable; +/** + * Marker interface to be used in exceptions related to syntactically correct but semantically invalid requests + */ interface UnprocessableRequest extends Throwable { } diff --git a/src/StatusCodeExtractionStrategy.php b/src/StatusCodeExtractionStrategy.php index 9086e90..fe4bb1c 100644 --- a/src/StatusCodeExtractionStrategy.php +++ b/src/StatusCodeExtractionStrategy.php @@ -5,6 +5,9 @@ use Throwable; +/** + * Defines how the translation of errors/exceptions into HTTP status codes should happen + */ interface StatusCodeExtractionStrategy { public function extractStatusCode(Throwable $error): int;