Apitte – správný content-type (JSON/XML) při error
- woytam
- Člen | 14
Ahoj,
snažím se vytvořit API s podporou JSON a XML, ale errory mi to vždy vyhodí
v JSON nezávisle na požadovaném formátu.
Například /api/v1/data.xml
vrací XML, ale pokud nejsem
přihlášen a chybu vyhodí RequestAuthenticationDecorator
,
odešle se JSON.
Používám Decorator
a Negotiation
pluginy.
Konfigurace:
services:
apiXmlTransformer:
create: Controller\ApiModule\V1\Transformers\XmlTransformer
tags: [apitte.negotiator.transformer: [suffix: xml, fallback: false]]
decorator.request.authentication:
class: Controller\ApiModule\V1\Decorators\RequestAuthenticationDecorator
tags: [apitte.core.decorator: [priority: 1, type: handler.before]]
decorator.response.example:
class: Controller\ApiModule\V1\Decorators\ExampleResponseDecorator
tags: [apitte.core.decorator: [priority: 50]]
decorator.response.exampleErr:
class: Controller\ApiModule\V1\Decorators\ExampleExceptionDecorator
tags: [apitte.core.decorator: [priority: 49]]
api:
debug: %debugMode%
plugins:
Apitte\Core\DI\Plugin\CoreDecoratorPlugin:
unification: true
Apitte\Negotiation\DI\NegotiationPlugin:
unification: on
Apitte\Core\DI\Plugin\CoreMappingPlugin:
resource:
resources:
Controller\ApiModule\V1\Controllers\:
# where the classes are located
paths: [%appDir%/ApiModule/V1/Controllers]
decorator:
inject: true
extensions:
resource: Contributte\DI\Extension\ResourceExtension
api: Apitte\Core\DI\ApiExtension
Když v RequestAuthenticationDecorator
mám
public function decorateRequest(ApiRequest $request, ApiResponse $response): ApiRequest {
throw new ClientErrorException('Invalid credentials, authentication failed.', 403);
}
odešle se vždy JSON nezávisle na nastaveném endpointu. I příkaz
throw new EarlyReturnResponseException($response)
odešle JSON.
Nepodařilo se mi nikde pořádně dohledat, jak nastavit Apitte, aby vždy
bralo v potaz požadovaný Content-Type
?
- Felix
- Nette Core | 1197
Pro budouci generace davam navod, my jsme to vyresili po emailu.
Tam je problem, ze logika, ktera se stara o prevadeni vystupnich dat do ruznych formatu nepocita s tim, ze by jsi ten error vyhodil jeste predtim nez doputuje do controlleru, resp. endpointu. Protoze podle controlleru, resp. endpointu, se pozna, jake podporuje koncovky. Tim, ze ten error vyhodis jeste predtim, tak se cela takhle logika nepouzije.
Nicmene, neni to tezky udelat, posilam ukazkovou tridu.
class ContentNegotiationExceptionDecorator implements IErrorDecorator
{
public function decorateError(ApiRequest $request, ApiResponse $response, ApiException $error): ApiResponse
{
if ($error instanceof ApiException) {
$code = $error->getCode();
$message = $error->getMessage();
} else {
$code = 500;
$message = 'Application encountered an internal error. Please try again later.';
}
$path = $request->getUri()->getPath();
if (Strings::endsWith($path, '.xml')) {
$response->getBody()->write('XML error');
$response = $response->withStatus($code);
throw new SnapshotException($error, $request, $response);
}
if (Strings::endsWith($path, '.json')) {
$response->getBody()->write('JSON error');
$response = $response->withStatus($code);
throw new SnapshotException($error, $request, $response);
}
return $response
->withStatus($code)
->withAttribute(ResponseAttributes::ATTR_ENTITY, ArrayEntity::from([
'status' => 'error',
'message' => $message,
]));
}
}
Predpokladam, ze z kodu je to patrne. Podle toho, jestli to konci na .xml nebo .json se vyhodi SnapshotException. Pokud se nic z toho nepouzije, tak to pokracuje jako predtim.
- woytam
- Člen | 14
Super, díky, funguje.
Nicméně bych se zeptal, zda neexistuje nějaké „úhlednější“
řešení. Přijde mi to tady, že řeším dvě stejné věci na dvou různých
místech. Tedy kontrolu požadovaného typu a následný převod na správný
formát.
To už mi skoro přijde lepší provádět kontrolu přihlášení až
v Controlleru.
A jen taková druhá otázečka, má nějaký smysl
if ($error instanceof ApiException)
když v nových verzích je stejně funkce definovaná již
s apiException
public function decorateError(ApiRequest $request, ApiResponse $response, ApiException $error)
Dříve bývalo Throwable
kde to smysl mělo.
- Felix
- Nette Core | 1197
Nicméně bych se zeptal, zda neexistuje nějaké „úhlednější“ řešení. Přijde mi to tady, že řeším dvě stejné věci na dvou různých místech. Tedy kontrolu požadovaného typu a následný převod na správný formát.
To už mi skoro přijde lepší provádět kontrolu přihlášení až v Controlleru.
Tak ono vracet data podle typu odpovedi (JSON a XML) je celkem narocna vec. Ale chapu te. Zkusim se zamyslet, jestli by to neslo nejak zjednodusit.
if ($error instanceof ApiException)
Uz to nema smysl. :-) Diky za hint.