Cannot generate IRI errors when enabling `rfc_7807_compliant_errors`
API Platform version(s) affected: 3.4.3
Description
I'm trying to enable rfc_7807_compliant_errors as an upgrade path to v4. When I do this, in my project, not only the error output changes (obviously), but I also get:
ApiPlatform\Metadata\Exception\InvalidArgumentException: Unable to generate an IRI for the item of type ....
How to reproduce
I have the following code:
<?php
namespace App;
#[ApiResource(
shortName: 'Organisation',
operations: [
new Get(
uriTemplate: '/organisations/{id}',
security: "is_granted('" . OrganisationVoter::ATTRIBUTE_ORGANISATION_READ . "', object)",
provider: ItemProvider::class,
),
new Get(
uriTemplate: '/users/{userId}/organisation',
uriVariables: [
'userId' => new Link(
fromClass: UserProjection::class,
),
],
normalizationContext: [
'item_uri_template' => '/organisations/{id}',
'groups' => [self::NORMALIZATION_GROUP],
],
provider: UserOrganisationProvider::class,
),
],
)]
class Organisation {}
I have two test cases:
- fetching an Organisation for a given user, and the user has access to the Organisation, it returns the Organisation
- fetching an Organisation for a given user, and the user does not have access to an Organisation, it returns HTTP 404.
The following happens:
- With
rfc_7807_compliant_errorstofalse, both test cases succeed. - With
rfc_7807_compliant_errorstotrue, the second test case throws:ApiPlatform\Metadata\Exception\InvalidArgumentException: Unable to generate an IRI for the item of type "App\Organisation". When I then remove the line'item_uri_template' => '/organisations/{id}',from the operation, the test succeeds again.
Additional Context
My (partial) config:
<?php
declare(strict_types=1);
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
use ApiPlatform\Validator\Exception\ValidationException;
use Doctrine\ORM\OptimisticLockException;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\Exception\ValidationFailedException;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->extension('api_platform', [
'defaults' => [
'collectDenormalizationErrors' => true,
'extra_properties' => [
'standard_put' => true,
'rfc_7807_compliant_errors' => true,
],
'pagination_client_enabled' => true,
'pagination_client_items_per_page' => true,
'pagination_items_per_page' => 30,
'pagination_maximum_items_per_page' => 100,
'stateless' => true,
],
'enable_link_security' => true,
'exception_to_status' => [
ExceptionInterface::class => Response::HTTP_BAD_REQUEST,
InvalidArgumentException::class => Response::HTTP_BAD_REQUEST,
OptimisticLockException::class => Response::HTTP_CONFLICT,
ValidationException::class => Response::HTTP_UNPROCESSABLE_ENTITY,
ValidationFailedException::class => Response::HTTP_UNPROCESSABLE_ENTITY,
],
'keep_legacy_inflector' => false,
'use_symfony_listeners' => true,
'serializer' => [
'hydra_prefix' => false,
],
]);
};
Full stacktrace:
ApiPlatform\Metadata\Exception\InvalidArgumentException: Unable to generate an IRI for the item of type "App\Organisation"
/home/www/app/vendor/api-platform/core/src/Symfony/Routing/IriConverter.php:194
/home/www/app/vendor/api-platform/core/src/Symfony/Routing/IriConverter.php:171
/home/www/app/vendor/api-platform/core/src/JsonLd/Serializer/ItemNormalizer.php:129
/home/www/app/vendor/api-platform/core/src/JsonLd/Serializer/ErrorNormalizer.php:31
/home/www/app/vendor/symfony/serializer/Serializer.php:150
/home/www/app/vendor/symfony/serializer/Serializer.php:129
/home/www/app/vendor/api-platform/core/src/State/Processor/SerializeProcessor.php:79
/home/www/app/vendor/api-platform/core/src/Symfony/EventListener/SerializeListener.php:102
/home/www/app/vendor/symfony/event-dispatcher/Debug/WrappedListener.php:115
/home/www/app/vendor/symfony/event-dispatcher/EventDispatcher.php:206
/home/www/app/vendor/symfony/event-dispatcher/EventDispatcher.php:56
/home/www/app/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:122
/home/www/app/vendor/symfony/http-kernel/HttpKernel.php:188
/home/www/app/vendor/symfony/http-kernel/HttpKernel.php:76
/home/www/app/vendor/symfony/http-kernel/EventListener/ErrorListener.php:97
/home/www/app/vendor/api-platform/core/src/Symfony/EventListener/ExceptionListener.php:50
/home/www/app/vendor/symfony/event-dispatcher/Debug/WrappedListener.php:115
/home/www/app/vendor/symfony/event-dispatcher/EventDispatcher.php:206
/home/www/app/vendor/symfony/event-dispatcher/EventDispatcher.php:56
/home/www/app/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:122
/home/www/app/vendor/symfony/http-kernel/HttpKernel.php:241
/home/www/app/vendor/symfony/http-kernel/HttpKernel.php:91
/home/www/app/vendor/symfony/http-kernel/Kernel.php:182
/home/www/app/vendor/symfony/http-kernel/HttpKernelBrowser.php:63
/home/www/app/vendor/symfony/framework-bundle/KernelBrowser.php:157
/home/www/app/vendor/symfony/browser-kit/AbstractBrowser.php:369
/home/www/app/vendor/api-platform/core/src/Symfony/Bundle/Test/Client.php:115
/home/www/app/tests/Functional/App/OrganisationTest.php:202
Caused by
ApiPlatform\Metadata\Exception\RuntimeException: Not able to retrieve identifiers.
/home/www/app/vendor/api-platform/core/src/Metadata/IdentifiersExtractor.php:139
/home/www/app/vendor/api-platform/core/src/Metadata/IdentifiersExtractor.php:90
/home/www/app/vendor/api-platform/core/src/Metadata/IdentifiersExtractor.php:60
/home/www/app/vendor/api-platform/core/src/Symfony/Routing/IriConverter.php:190
/home/www/app/vendor/api-platform/core/src/Symfony/Routing/IriConverter.php:171
/home/www/app/vendor/api-platform/core/src/JsonLd/Serializer/ItemNormalizer.php:129
/home/www/app/vendor/api-platform/core/src/JsonLd/Serializer/ErrorNormalizer.php:31
/home/www/app/vendor/symfony/serializer/Serializer.php:150
/home/www/app/vendor/symfony/serializer/Serializer.php:129
/home/www/app/vendor/api-platform/core/src/State/Processor/SerializeProcessor.php:79
/home/www/app/vendor/api-platform/core/src/Symfony/EventListener/SerializeListener.php:102
/home/www/app/vendor/symfony/event-dispatcher/Debug/WrappedListener.php:115
/home/www/app/vendor/symfony/event-dispatcher/EventDispatcher.php:206
/home/www/app/vendor/symfony/event-dispatcher/EventDispatcher.php:56
/home/www/app/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:122
/home/www/app/vendor/symfony/http-kernel/HttpKernel.php:188
/home/www/app/vendor/symfony/http-kernel/HttpKernel.php:76
/home/www/app/vendor/symfony/http-kernel/EventListener/ErrorListener.php:97
/home/www/app/vendor/api-platform/core/src/Symfony/EventListener/ExceptionListener.php:50
/home/www/app/vendor/symfony/event-dispatcher/Debug/WrappedListener.php:115
/home/www/app/vendor/symfony/event-dispatcher/EventDispatcher.php:206
/home/www/app/vendor/symfony/event-dispatcher/EventDispatcher.php:56
/home/www/app/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:122
/home/www/app/vendor/symfony/http-kernel/HttpKernel.php:241
/home/www/app/vendor/symfony/http-kernel/HttpKernel.php:91
/home/www/app/vendor/symfony/http-kernel/Kernel.php:182
/home/www/app/vendor/symfony/http-kernel/HttpKernelBrowser.php:63
/home/www/app/vendor/symfony/framework-bundle/KernelBrowser.php:157
/home/www/app/vendor/symfony/browser-kit/AbstractBrowser.php:369
/home/www/app/vendor/api-platform/core/src/Symfony/Bundle/Test/Client.php:115
/home/www/app/tests/Functional/App/OrganisationTest.php:202
Caused by
Symfony\Component\HttpKernel\Exception\NotFoundHttpException: Not Found
/home/www/app/vendor/api-platform/core/src/State/Provider/ReadProvider.php:94
/home/www/app/vendor/api-platform/core/src/Symfony/Validator/State/ParameterValidatorProvider.php:87
/home/www/app/vendor/api-platform/core/src/State/Provider/ParameterProvider.php:103
/home/www/app/vendor/api-platform/core/src/Symfony/Bundle/SwaggerUi/SwaggerUiProvider.php:50
/home/www/app/vendor/api-platform/core/src/Symfony/Security/State/AccessCheckerProvider.php:62
/home/www/app/vendor/api-platform/core/src/State/Provider/SecurityParameterProvider.php:39
/home/www/app/vendor/api-platform/core/src/Symfony/Security/State/LinkedReadProvider.php:42
/home/www/app/vendor/api-platform/core/src/Symfony/Security/State/LinkAccessCheckerProvider.php:40
/home/www/app/vendor/api-platform/core/src/Symfony/EventListener/ReadListener.php:95
/home/www/app/vendor/symfony/event-dispatcher/Debug/WrappedListener.php:115
/home/www/app/vendor/symfony/event-dispatcher/EventDispatcher.php:206
/home/www/app/vendor/symfony/event-dispatcher/EventDispatcher.php:56
/home/www/app/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:122
/home/www/app/vendor/symfony/http-kernel/HttpKernel.php:159
/home/www/app/vendor/symfony/http-kernel/HttpKernel.php:76
/home/www/app/vendor/symfony/http-kernel/Kernel.php:182
/home/www/app/vendor/symfony/http-kernel/HttpKernelBrowser.php:63
/home/www/app/vendor/symfony/framework-bundle/KernelBrowser.php:157
/home/www/app/vendor/symfony/browser-kit/AbstractBrowser.php:369
/home/www/app/vendor/api-platform/core/src/Symfony/Bundle/Test/Client.php:115
/home/www/app/tests/Functional/App/OrganisationTest.php:202
Hi @darthf1 API Platform was not able to gather identifiers from Organization, can you maybe api-resource:debug the first operation and check if it has uriVariables? I have no explanation for why the flag has this impact, what I usually do to debug exceptions is to dump directly inside the ErrorListener, my guess is that an exception is thrown no matter the rfc_7807_compliant_errors flag.
What I found so far (I put some breakpoints in my app):
- This line is called https://github.com/api-platform/core/blob/main/src/State/Provider/ReadProvider.php#L93, because
$dataisnull - Then I end up at https://github.com/symfony/http-kernel/blob/7.1/HttpKernel.php#L241
- When I set
rfc_7807_compliant_errorstofalse, and i press "Step Over", I go to the next line and a few clicks later it responds with a 404. - When I set
rfc_7807_compliant_errorstotrue, and i press "Step Over", I somehow end up again in theReadProvider, but this time $data is not null but an Error resource.
- Then I'm here https://github.com/api-platform/core/blob/main/src/Symfony/Routing/IriConverter.php#L187
- And then, ultimately, I end up here https://github.com/api-platform/core/blob/main/src/Symfony/Routing/IriConverter.php#L191. Reason, the item uri template expects an
iduri variable, but there is none provided in the context (maybe because there was no resource found initially?):
but you stil lget a 404? the error at:
Looks just fine to me
Looks just fine to me
Yes absolutely! But that error is not handled gracefully anymore.
My tests does:
public function testGetOwnOrganisationWhenNotMemberOfOrganisationThrowsHttpNotFound(): void
{
$referenceRepository = self::loadFixtures([
UserTestFixture::class,
OrganisationTestFixture::class,
]);
$user3 = $referenceRepository->getReference(UserTestFixture::USER_3, User::class);
$client = self::createAuthenticatedClient('[email protected]', 'user_3');
$client->request('GET', \sprintf('/v1/users/%s/organisation', $user3->getId()));
self::assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND);
}
- With
rfc_7807_compliant_errors=falsethis tests succeeds - With
rfc_7807_compliant_errors=truethis test fails, and throws
ApiPlatform\Metadata\Exception\InvalidArgumentException: Unable to generate an IRI for the item of type "UserInterface\Organisation\Rest\OrganisationProjection"
- With
rfc_7807_compliant_errors=trueand'item_uri_template' => '/organisations/{id}',removed from the operationsnormalizationContext, this test succeeds as well.
May you provide a reproducer please?
Quite hard to track, got it!
https://github.com/api-platform/core/pull/6816 Thanks for the bug report!
Quite hard to track, got it!
https://github.com/api-platform/core/pull/6816 Thanks for the bug report!
That is great! Thanks a lot.