I’ve been working on the Apigility on Expressive initiative, and the first step, as it was with Apigility originally, was setting up error handling. To this end, I’ve created a new problem-details module that provides the following:
- A Problem Details exception interface, providing access to the title, type, detail, status code, and any additional data as key/value pairs (
getAdditionaData()); a trait accompanies this interface for implementing the methods, but users would be expected to create their own custom types with named constructors for reporting problems.
Problem Details responses, both XML and JSON formats. Each has named constructors for generating the response from either discrete arguments or throwables; an additional, optional flag on the
fromThrowable()method controls whether exception backtrace/previous exception details are presented. If the throwable is of the problem details exception type, it pulls information from the exception to create the problem details information.
- A factory for generating a Problem Details response that uses the
value of the PSR-7 Accept headerPSR-7
ServerRequestInterfaceand its composed
Acceptheader (if present) to determine whether XML or JSON should be returned (defaults to XML when unable to match). This has separate
named constructors that mirror those of the responsesfactory methods for generating a response from PHP values vs PHP
Throwableinstances. The factory composes a flag for
$isDebugmode (in which case full exception details are provided), JSON encoding flags, a PSR-7 response prototype, and a callable factory for generating a writable PSR-7 stream for use with the response body.
- Middleware that performs error handling. Caught errors/exceptions are passed to the the above factory, along with the
Accept headerrequest instance. The middleware can compose a flag indicating whether or not to include debug detailscomposes a problem details response factory instance.
Why so many features?
The easiest path is to simply return a problem details response from your middleware. However, this can be tedious, and you may miss the fact that code you are calling raises exceptions, leaving you with error responses in HTML instead of JSON or XML. As such, having error handling middleware specific to your API can be a good fallback.
If you’re going to have error handling middleware anyways, why not just raise exceptions in the first place? Thus, the special exception interface. One advantage of this exception type is that you can create custom exceptions for your application that compose implement it; if you are in an API context, you get excellent problem reporting; if you are in your standard application context, you’ll get your normal errors. Extra context with exceptions is always useful.
Finally, one thing we never tackled with Apigility was using XML. The problem details specification defines both JSON and XML formats, and this seems like a good time to start implementing the latter. Considering we have access to the Accept header in middleware, content negotiation is not difficult, and would give us more possibilities.
Do you have any feedback? Am I missing anything? Does the order of arguments make sense? etc.
For full documentation: https://weierophinney.github.io/problem-details/