for transformations of response after it is returned from endpoint.

They could e.g. add some useful data, transform them or handle request globally (e.g. for authorization).

# Setup

api:
    plugins:
        Apitte\Core\DI\Plugin\CoreDecoratorPlugin:
1
2
3

Decorator plugin overrides JsonDispatcher with DecoratedDispatcher

# Register decorators

services:
    decorator.request.authentication:
        class: App\Api\Decorator\ExampleResponseDecorator
        tags: [apitte.core.decorator: [priority: 50]]
1
2
3
4

Each decorator could have tag apitte.core.decorator with priority defined.

  • Decorator with lowest priority number is called first.
  • Default priority is 10

# Request decorators

Add some data to request or return response through EarlyReturnResponseException.

# RequestParameterDecorator and RequestEntityDecorator

Enable mapping of request parameters and entities.

Registered with priorities 100 and 101.

See mapping chapter for more info.

# Implementing request decorator

# MetadataDecorator

services:
    decorator.request.metadata:
        class: App\Api\Decorator\RequestMetadataDecorator
        tags: [apitte.core.decorator: [priority: 50]
1
2
3
4
namespace App\Api\Decorator;

use Apitte\Core\Decorator\IRequestDecorator;
use Apitte\Core\Exception\Runtime\EarlyReturnResponseException;
use Apitte\Core\Http\ApiRequest;
use Apitte\Core\Http\ApiResponse;

class RequestMetadataDecorator implements IRequestDecorator
{

    /**
     * @throws EarlyReturnResponseException If other request decorators and also deeper layers (endpoint) should be skipped
     */
    public function decorateRequest(ApiRequest $request, ApiResponse $response): ApiRequest
    {
        // Do something with request (e.g. add useful attributes for endpoint)
        $request = $request->withAttribute('attributeName', 'attributeValue');

        return $request;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# AuthenticationDecorator

services:
    decorator.request.authentication:
        class: App\Api\Decorator\RequestAuthenticationDecorator
        tags: [apitte.core.decorator: [priority: 1, type: handler.before]]
1
2
3
4
namespace App\Api\Decorator;

use Apitte\Core\Decorator\IRequestDecorator;
use Apitte\Core\Exception\Runtime\EarlyReturnResponseException;
use Apitte\Core\Http\ApiRequest;
use Apitte\Core\Http\ApiResponse;
use function GuzzleHttp\Psr7\stream_for;

class RequestAuthenticationDecorator implements IRequestDecorator
{

    /**
     * @throws EarlyReturnResponseException If other request decorators and also deeper layers (endpoint) should be skipped
     */
    public function decorateRequest(ApiRequest $request, ApiResponse $response): ApiRequest
    {
        if ($userAuthenticationFailed) {
            $body = stream_for(json_encode([
                'status' => 'error',
                'code' => 403,
                'message' => 'Invalid credentials, authentication failed.'
            ]));

            $response = $response
                ->withStatus(403)
                ->withBody($body);
            throw new EarlyReturnResponseException($response);
        }

        return $request;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

Tip

You could also authenticate only some endpoints thanks to tags and metadata from SimpleRouter.

use Apitte\Core\Http\RequestAttributes;
use Apitte\Core\Schema\Endpoint;

/** @var Endpoint $endpoint Schema of matched endpoint */
$endpoint = $request->getAttribute(RequestAttributes::ATTR_ENDPOINT);

if ($endpoint->hasTag('noAuthentication')) {
    // Don't authenticate
}
1
2
3
4
5
6
7
8
9

# Response decorators

Modify response returned from endpoint.

You could return response through EarlyReturnResponseException so other response decorators are not used.

# ResponseEntityDecorator

Transforms data into format requested in Accept header and in url suffix (/api/v1/users.xml)

See negotiation chapter for details.

# Implementing response decorator

namespace App\Api\Decorator;

use Apitte\Core\Decorator\IResponseDecorator;
use Apitte\Core\Exception\Runtime\EarlyReturnResponseException;
use Apitte\Core\Http\ApiRequest;
use Apitte\Core\Http\ApiResponse;

class ExampleResponseDecorator implements IResponseDecorator
{

    /**
     * @throws EarlyReturnResponseException If other response decorators should be skipped
     */
    public function decorateResponse(ApiRequest $request, ApiResponse $response): ApiResponse
    {
        // Do something with response (e.g. transform data)
        return $response;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Exception decorators

Transforms error into response.

If an exception decorator return null instead of response then error is handled by internal error handler

# ResponseEntityDecorator

Transforms error into format requested in Accept header and in url suffix (/api/v1/users.xml)

See negotiation chapter for details.

# Implementing exception decorator

namespace App\Api\Decorator;

use Apitte\Core\Decorator\IErrorDecorator;
use Apitte\Core\Http\ApiRequest;
use Apitte\Core\Http\ApiResponse;
use Throwable;

class ExampleExceptionDecorator implements IErrorDecorator
{

    public function decorateError(ApiRequest $request, ApiResponse $response, Throwable $error): ApiResponse
    {
        $response = $this->errorToResponse($response, $error);
        return $response;
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18