I have a very simple custom API endpoint with an input and output DTO and a Symfony Controller, build like the examples in the documentation page: https://api-platform.com/docs/v2.3/core/dto/
#[AsController]
class MessageDataGetInvokeController extends AbstractController
{
#[Route(
path: '/api/messages',
name: 'messages_get_invoke',
defaults: [
'_api_respond' => true,
'_api_normalization_context' => ['api_sub_level' => true]
],
methods: ['GET']
)]
public function __invoke(
#[MapQueryString] MessageReadInputDto $messageReadInputDto
): MessageReadOutputDto {
return new MessageReadOutputDto(
data: 'test invoke controller: ' . $messageReadInputDto->getBusinessKey()
);
}
}
readonly class MessageReadInputDto
{
public function __construct(
#[Groups(['custom:read'])]
#[ApiProperty(example: 'b19d262e-ad00-4c57-8a4d-dbcbe294da8e')]
public string $businessKey
) {
}
public function getBusinessKey(): string
{
return $this->businessKey;
}
}
readonly class MessageReadOutputDto
{
public function __construct(
#[Groups(['custom:read'])]
public string $data
) {
}
public function getData(): string
{
return $this->data;
}
}
#[ApiResource(
operations: [
new Get(
output: MessageReadOutputDto::class,
openapiContext: [
'summary' => 'Retrieve messages',
'description' => 'This endpoint retrieves a message by given input data',
'parameters' => [
[
'name' => 'businessKey',
'in' => 'query',
'description' => 'Business process identifier',
'required' => true,
'type' => 'string',
'default' => '',
],
],
],
routeName: 'messages_get_invoke',
normalizationContext: ['groups' => ['custom:read']],
read: false
),
new Post(),
new Put(),
new Delete()
],
normalizationContext: ['groups' => ['custom:read']],
denormalizationContext: ['groups' => ['custom:write']],
)]
class Message
{
}
And when I call the endpoint I receive this error:
{
"@id": "/api/errors",
"@type": "hydra:Error",
"title": "An error occurred",
"detail": "Call to a member function serialize() on null",
"status": 500,
"type": "/errors/500",
"trace": [
{
"file": "/var/www/html/vendor/api-platform/core/src/Symfony/EventListener/SerializeListener.php",
"line": 133,
"function": "serializeRawData",
"class": "ApiPlatform\\Symfony\\EventListener\\SerializeListener",
"type": "->"
},
{
"file": "/var/www/html/vendor/symfony/event-dispatcher/Debug/WrappedListener.php",
"line": 115,
"function": "onKernelView",
"class": "ApiPlatform\\Symfony\\EventListener\\SerializeListener",
"type": "->"
},
My api-platform config:
api_platform:
title: Hello API Platform
version: 1.0.0
use_symfony_listeners: true
formats:
jsonld: ['application/ld+json']
json: ['application/json']
docs_formats:
jsonld: ['application/ld+json']
jsonopenapi: ['application/vnd.openapi+json']
html: ['text/html']
defaults:
rfc_7807_compliant_errors: true
stateless: true
cache_headers:
vary: ['Content-Type', 'Authorization', 'Origin']
extra_properties:
standard_put: true
rfc_7807_compliant_errors: true
event_listeners_backward_compatibility_layer: false
keep_legacy_inflector: false
What exactly is missing? Thanks!
The solution was easy, but really not so good documented in the API Platform documentation. If someone from the API Platform reads this, then please make an update.
The issue was, that the Ressource class was not visible and thats why there is no serializer. To fix the issue you have to add the Ressource class in that way:
#[AsController]
class MessageDataGetInvokeController extends AbstractController
{
#[Route(
path: '/api/messages',
name: 'messages_get',
defaults: [
'_api_respond' => true,
'_api_resource_class' => Message::class,
],
methods: ['GET']
)]
public function __invoke(
#[MapQueryString] MessageReadInputDto $messageReadInputDto
): MessageReadOutputDto {
return new MessageReadOutputDto(
data: 'test invoke controller: ' . $messageReadInputDto->getBusinessKey()
);
}
}
In this way, a Serializer processes the returned DTO and everything works.