gpt4 book ai didi

error-handling - 如果已经存在,则返回实体-API平台

转载 作者:行者123 更新时间:2023-12-03 07:57:17 25 4
gpt4 key购买 nike

我有实体Tag,它具有唯一的属性tagValue。当我用已经存在的POST制作tagValue时,我想获得它作为响应。
config/validator/tag.yaml:

App\Entity\Tag:
constraints:
- Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity: tagValue
properties:
tagValue:
- NotBlank: ~

src/Entity/Tag.php:

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use DateTimeInterface;
use DateTime;
use Exception;

/**
* @ORM\Table(name="tag")
* @ORM\Entity(repositoryClass="App\Repository\TagRepository")
* @ORM\HasLifecycleCallbacks
*/
class Tag
{
/**
* @var int
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
* @ORM\Column(type="integer")
*/
private $id;

/**
* @var string
* @ORM\Column(type="string", length=255)
*/
private $tagValue;

// ...
}


当我制作 POST时:

curl --request POST \
--url http://127.0.0.1:8888/api/tags \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'x-auth-token: xxxxxxxxxxxxxxxx' \
--data '{
"tagValue": "test"
}'

我得到了刚创建的实体和代码201的响应。一切正常,但是,如果我如预期的那样再次发出此请求,我将获得带有响应主体的响应代码400:
{
"type": "https:\/\/tools.ietf.org\/html\/rfc2616#section-10",
"title": "An error occurred",
"detail": "tagValue: This value is already used.",
"violations": [
{
"propertyPath": "tagValue",
"message": "This value is already used."
}
]
}

但是我希望存在实体对此做出回应。

有什么想法如何做到这一点而不破坏REST规则?

(Symfony 4.2.5,api-platform/api-pack 1.2.0)

最佳答案

最终,我从maks-rafalko在GitHub上得到了一个答案(对此我非常感激),如果有人坚持同样的问题,这里是his solution:

You are lucky man, we have just implemented it inside our application. There is no built in functionality in API-Platform for this feature, we had to override some classes in order to add it.



首先,这是当唯一性约束无效时我们的响应现在的样子:
{
"type": "https://tools.ietf.org/html/rfc2616#section-10",
"title": "An error occurred",
"detail": "number: This Usage Reference already exists with the same number and channel.",
"violations": [
{
"propertyPath": "number",
"message": "This Usage Reference already exists with the same number and channel."
}
],
"existingUniqueEntities": [
{
"uniquePropertyPaths": [
"number",
"channel"
],
"entity": {
"id": 1101,
"number": "23423423435",
"channel": "/api/channels/1",
"createdAt": "2019-07-17T07:25:50.721Z"
}
}
]
}

请注意,您可能有很多独特的违规行为,这种模式可以返回许多已经存在并与提供的请求冲突的实体(例如,实体可以具有两对唯一键,一对通过电子邮件,另一对通过引用) )

此外,我们的实现完全使用执行GET/resource时将使用的序列化组,其中resource是您要创建的资源。我们从api平台元数据中获得那些序列化组

所以这是代码:
<?php

declare(strict_types=1);

namespace App\Serializer;

use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Serializer\AbstractConstraintViolationListNormalizer;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\ConstraintViolationListInterface;

/**
* This class completely overrides `ApiPlatform\Core\Problem\Serializer\ConstraintViolationListNormalizer` class
* since it's final
*
* Goal of overriding is to add `existingUniqueEntities` key when ViolationList contains unique entity violations
*
* @see \ApiPlatform\Core\Problem\Serializer\ConstraintViolationListNormalizer
*/
class ConstraintViolationListNormalizer extends AbstractConstraintViolationListNormalizer implements NormalizerAwareInterface
{
public const FORMAT = 'jsonproblem';
public const TYPE = 'type';
public const TITLE = 'title';

/**
* @var array<string, string>
*/
private $defaultContext = [
self::TYPE => 'https://tools.ietf.org/html/rfc2616#section-10',
self::TITLE => 'An error occurred',
];

/**
* @var ResourceMetadataFactoryInterface
*/
private $resourceMetadataFactory;

/**
* @var SerializerInterface
*/
private $serializer;

/**
* @var NormalizerInterface
*/
private $normalizer;

public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, array $serializePayloadFields = null, NameConverterInterface $nameConverter = null, array $defaultContext = [])
{
parent::__construct($serializePayloadFields, $nameConverter);

$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
}

public function setNormalizer(NormalizerInterface $normalizer): void
{
$this->normalizer = $normalizer;
}

/**
* @param mixed $object
* @param string|null $format
* @param array $context
*
* @return array
*/
public function normalize($object, $format = null, array $context = []): array
{
[$messages, $violations] = $this->getMessagesAndViolations($object);

$response = [
'type' => $context[self::TYPE] ?? $this->defaultContext[self::TYPE],
'title' => $context[self::TITLE] ?? $this->defaultContext[self::TITLE],
'detail' => $messages ? implode("\n", $messages) : (string) $object,
'violations' => $violations,
];

$existingUniqueEntities = $this->getExistingUniqueEntities($object);

return \count($existingUniqueEntities) > 0 ?
array_merge($response, ['existingUniqueEntities' => $existingUniqueEntities])
: $response;
}

private function getExistingUniqueEntities(ConstraintViolationListInterface $constraintViolationList): array
{
$existingUniqueEntities = [];

/** @var ConstraintViolation $violation */
foreach ($constraintViolationList as $violation) {
$constraint = $violation->getConstraint();

if (!$constraint instanceof UniqueEntity) {
continue;
}

$rootEntity = \is_object($violation->getRoot()) ? $violation->getRoot() : null;

if ($rootEntity === null) {
continue;
}

$existingEntityCausedViolation = $violation->getCause()[0];

$metadata = $this->resourceMetadataFactory->create(\get_class($existingEntityCausedViolation));

// get normalization groups for `GET /resource` operation, fallback to global resource groups
$normalizationContext = $metadata->getItemOperationAttribute('get', 'normalization_context', [], true);
$groups = $normalizationContext['groups'] ?? [];
$entityNormalizationContext = \count($groups) > 0 ? ['groups' => $groups] : [];

$existingUniqueEntities[] = [
'uniquePropertyPaths' => $constraint->fields,
'entity' => $this->normalizer->normalize($existingEntityCausedViolation, null, $entityNormalizationContext),
];
}

return $existingUniqueEntities;
}
}

一切都在getExistingUniqueEntities内部,但是不幸的是,我们不得不完全重写ApiPlatform \ Core \ Problem \ Serializer \ ConstraintViolationListNormalizer类,因为它是最终的,无法扩展。

我们设法使用Compiler Pass覆盖了它:
# src/Kernel.php

class Kernel extends BaseKernel implements CompilerPassInterface
{

private const CONSTRAINT_VIOLATION_LIST_NORMALIZER_PRIORITY = -780;

...

public function process(ContainerBuilder $container)
{
...
$constraintViolationListNormalizerDefinition = new Definition(
ConstraintViolationListNormalizer::class,
[
$container->getDefinition('api_platform.metadata.resource.metadata_factory.cached'),
$container->getParameter('api_platform.validator.serialize_payload_fields'),
$container->hasDefinition('api_platform.name_converter') ? $container->getDefinition('api_platform.name_converter') : null,
[],
]
);
$constraintViolationListNormalizerDefinition->addTag('serializer.normalizer', ['priority' => self::CONSTRAINT_VIOLATION_LIST_NORMALIZER_PRIORITY]);

$container->setDefinition('api_platform.problem.normalizer.constraint_violation_list', $constraintViolationListNormalizerDefinition);
}

So, this solution is based on Symfony Validator and "listens" UniqueEntity vailoations. And if there are such violations, this normalizer adds already existing entity(ies) to the response.

Hope it helps!

关于error-handling - 如果已经存在,则返回实体-API平台,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57310654/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com