Skip to content

Commit 09a87b0

Browse files
committed
Merge branch 'hotfix/18'
Close #18
2 parents 35fd871 + 56d9e3c commit 09a87b0

File tree

2 files changed

+215
-6
lines changed

2 files changed

+215
-6
lines changed

README.md

Lines changed: 214 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,219 @@
1-
Api-Problem: ZF2 Module for API-Problem representations
2-
=======================================================
1+
ZF Api Problem
2+
==============
33

44
[![Build Status](https://travis-ci.org/zfcampus/zf-api-problem.png)](https://travis-ci.org/zfcampus/zf-api-problem)
5-
[![Coverage Status](https://coveralls.io/repos/zfcampus/zf-api-problem/badge.png?branch=master)](https://coveralls.io/r/zfcampus/zf-api-problem)
5+
6+
Introduction
7+
------------
68

79
This module provides data structures and rendering for the API-Problem format.
810

9-
- [Problem API](http://tools.ietf.org/html/draft-nottingham-http-problem-05),
10-
used for reporting API problems
11+
- [Problem Details for HTTP APIs](http://tools.ietf.org/html/draft-nottingham-http-problem-06),
12+
used for reporting API problems.
13+
14+
Installation
15+
------------
16+
17+
Run the following `composer` command:
18+
19+
```console
20+
$ composer require "zfcampus/zf-api-problem:~1.0-dev"
21+
```
22+
23+
Alternately, manually add the following to your `composer.json`, in the `require` section:
24+
25+
```javascript
26+
"require": {
27+
"zfcampus/zf-api-problem": "~1.0-dev"
28+
}
29+
```
30+
31+
And then run `composer update` to ensure the module is installed.
32+
33+
Finally, add the module name to your project's `config/application.config.php` under the `modules`
34+
key:
35+
36+
```php
37+
return array(
38+
/* ... */
39+
'modules' => array(
40+
/* ... */
41+
'ZF\ApiProblem',
42+
),
43+
/* ... */
44+
);
45+
```
46+
47+
Configuration
48+
-------------
49+
50+
### User Configuration
51+
52+
The top-level configuration key for user configuration of this module is `zf-api-problem`.
53+
54+
#### Key: `accept_filters`
55+
56+
// Accept types that should allow ApiProblem responses
57+
58+
#### Key: `render_error_controllers`
59+
60+
// Array of controller service names that should enable the ApiProblem render.error listener
61+
62+
63+
### System Configuration
64+
65+
The following configuration is provided in `config/module.config.php` to enable the module to
66+
function:
67+
68+
```php
69+
'service_manager' => array(
70+
'aliases' => array(
71+
'ZF\ApiProblem\ApiProblemListener' => 'ZF\ApiProblem\Listener\ApiProblemListener',
72+
'ZF\ApiProblem\RenderErrorListener' => 'ZF\ApiProblem\Listener\RenderErrorListener',
73+
'ZF\ApiProblem\ApiProblemRenderer' => 'ZF\ApiProblem\View\ApiProblemRenderer',
74+
'ZF\ApiProblem\ApiProblemStrategy' => 'ZF\ApiProblem\View\ApiProblemStrategy',
75+
),
76+
'factories' => array(
77+
'ZF\ApiProblem\Listener\ApiProblemListener' => 'ZF\ApiProblem\Factory\ApiProblemListenerFactory',
78+
'ZF\ApiProblem\Listener\RenderErrorListener' => 'ZF\ApiProblem\Factory\RenderErrorListenerFactory',
79+
'ZF\ApiProblem\Listener\SendApiProblemResponseListener' => 'ZF\ApiProblem\Factory\SendApiProblemResponseListenerFactory',
80+
'ZF\ApiProblem\View\ApiProblemRenderer' => 'ZF\ApiProblem\Factory\ApiProblemRendererFactory',
81+
'ZF\ApiProblem\View\ApiProblemStrategy' => 'ZF\ApiProblem\Factory\ApiProblemStrategyFactory',
82+
),
83+
),
84+
'view_manager' => array(
85+
// Enable this in your application configuration in order to get full
86+
// exception stack traces in your API-Problem responses.
87+
'display_exceptions' => false,
88+
),
89+
```
90+
91+
ZF2 Events
92+
----------
93+
94+
### Listeners
95+
96+
#### `ZF\ApiProblem\Listener\ApiProblemListener`
97+
98+
The `ApiProblemListener` attaches to three events in the MVC lifecycle:
99+
100+
- `MvcEvent::EVENT_DISPATCH` as a _shared_ listener on `Zend\Stdlib\DispatchableInterface` with a
101+
priority of `100`.
102+
- `MvcEvent::EVENT_DISPATCH_ERROR` with a priority of `100`.
103+
- `MvcEvent::EVENT_RENDER` with a priority of `1000`.
104+
105+
If the current `Accept` media type does not match the configured API-Problem media types (by
106+
default, these are `application/json` and `application/*+json`), then this listener returns without
107+
taking any action.
108+
109+
When this listener does take action, the purposes are threefold:
110+
111+
- Before dispatching, the `render_error_controllers` configuration value is consulted to determine
112+
if the `ZF\ApiProblem\Listener\RenderErrorListener` should be attached; see
113+
[RenderErrorListener](#rendererrorlistener) for more information.
114+
- After dispatching, detects the type of response from the controller; if it is already an
115+
`ApiProblem` model, it continues without doing anything. If an exception was thrown during
116+
dispatch, it converts the response to an API-Problem response with some information from the
117+
exception.
118+
- If a dispatch error occurred, and the `Accept` type is in the set defined for API-Problems, it
119+
attempts to cast the dispatch exception into an API-Problem response.
120+
121+
#### `ZF\ApiProblem\Listener\RenderErrorListener`
122+
123+
This listener is attached to `MvcEvent::EVENT_RENDER_ERROR` at priority `100`. This listener is
124+
conditionally attached by `ZF\ApiProblem\Listener\ApiProblemListener` for controllers that require
125+
API Problem responses. With a priority of `100`, this ensures that this listener runs before the
126+
default ZF2 listener on this event. In cases when it does run, it will cast an exception into an
127+
API-problem response.
128+
129+
#### `ZF\ApiProblem\Listener\SendApiProblemResponseListener`
130+
131+
This listener is attached to `SendResponseEvent::EVENT_SEND_RESPONSE` at priority `-500`. The
132+
primary purpose of this listener is, on detection of an API-Problem response, to send appropriate
133+
headers and the problem details as the content body. If the `view_manager`'s `display_exceptions`
134+
setting is enabled, the listener will determine if the API-Problem represents an application
135+
exception, and, if so, inject the exception trace as part of the serialized response.
136+
137+
ZF2 Services
138+
------------
139+
140+
### Event Services
141+
142+
- `ZF\ApiProblem\Listener\ApiProblemListener`
143+
- `ZF\ApiProblem\Listener\RenderErrorListener`
144+
- `ZF\ApiProblem\Listener\SendApiProblemResponseListener`
145+
146+
### View Services
147+
148+
#### `ZF\ApiProblem\View\ApiProblemRenderer`
149+
150+
This service extends the `JsonRenderer` service from the ZF2 MVC layer. Its primary responsibility
151+
is to decorate JSON rendering with the ability to optionally output stack traces.
152+
153+
#### `ZF\ApiProblem\View\ApiProblemStrategy`
154+
155+
This service is a view strategy that detects a `ZF\ApiProblem\View\ApiProblemModel`; when detected,
156+
it selects the [ApiProblemRender](#zfapiproblemviewapiproblemrenderer), and injects the response
157+
with a `Content-Type` header that contains the `application/problem+json` media type. This is
158+
similar in nature to Zend Framework 2's `JsonStrategy`.
159+
160+
### Models
161+
162+
#### `ZF\ApiProblem\ApiProblem`
163+
164+
An instance of `ZF\ApiProblem\ApiProblem` serves the purpose of modeling the kind of problem that is
165+
encountered. An instance of `ApiProblem` is typically wrapped in an
166+
[ApiProblemResponse](#zfapiproblemapiproblemresponse). Most information can be passed into the
167+
constructor:
168+
169+
```php
170+
class ApiProblem {
171+
public function __construct(
172+
$status,
173+
$detail,
174+
$type = null,
175+
$title = null,
176+
array $additional = array()
177+
) {
178+
/* ... */
179+
}
180+
}
181+
```
182+
183+
For example:
184+
185+
```php
186+
new ApiProblem(404, 'Entity not found');
187+
188+
// or
189+
190+
new ApiProblem(424, $exceptionInstance);
191+
```
192+
193+
#### `ZF\ApiProblem\ApiProblemResponse`
194+
195+
An instance of `ZF\ApiProblem\ApiProblemResponse` can be returned from any controller service or ZF2
196+
MVC event in order to short-circuit the MVC lifecycle and immediately return a response. When it
197+
is, the response will be converted to the proper JSON structure for an API-Problem, and the
198+
`Content-Type` header will be set to the `application/problem+json` media type.
199+
200+
For example:
201+
202+
```php
203+
use Zend\Mvc\Controller\AbstractActionController;
204+
use ZF\ApiProblem\ApiProblem;
205+
use ZF\ApiProblem\ApiProblemResponse;
206+
207+
class MyController extends AbstractActionController
208+
{
209+
/* ... */
210+
public function fetch($id)
211+
{
212+
$entity = $this->model->fetch($id);
213+
if (! $entity) {
214+
return new ApiProblemResponse(ApiProblem(404, 'Entity not found'));
215+
}
216+
return $entity;
217+
}
218+
}
219+
```

src/Listener/ApiProblemListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public function onDispatch(MvcEvent $e)
143143
/**
144144
* Handle render errors
145145
*
146-
* If the event representes an error, and has an exception composed, marshals an ApiProblem
146+
* If the event represents an error, and has an exception composed, marshals an ApiProblem
147147
* based on the exception, stops event propagation, and returns an ApiProblemResponse.
148148
*
149149
* @param MvcEvent $e

0 commit comments

Comments
 (0)