使用 Symfony 序列化程序组件时在反序列化期间处理不正确的数据

Handling imporper data during deserialization when using Symfony Serializer Component

我是 Symfony 序列化器组件的新手。我正在尝试将 JSON 正文正确反序列化为以下 DTO:

class PostDTO
{
    /** @var string */
    private $name;

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName(string $name): void
    {
        $this->name = $name;
    }
}

控制器方法如下:

/**
 * @Route (path="", methods={"POST"}, name="new_post")
 * @param Request $request
 * @return Response
 */
public function create(Request $request): Response
{
    $model = $this->serializer->deserialize($request->getContent(), PostDTO::class, 'json');
    // call the service with the model
    return new JsonResponse();
}

我的问题是我想在正文反序列化后处理业务验证。但是,如果我为名称指定了一个无效值,例如 false[],反序列化将失败并出现异常:Symfony\Component\Serializer\Exception\NotNormalizableValueException: "The type of the "name" attribute for class "App\Service\PostDTO" must be one of "string" ("array" given)..

我明白是因为我故意设置了"name": []。但是,我一直在寻找一种方法来将字段设置为默认值,甚至执行一些验证预反序列化。

我找到了处理这个问题的正确方法。抛出该异常是因为序列化程序无法使用我提供的无效负载创建 PostDTO class。

为了解决这个问题,我创建了我的自定义非规范化器,它只针对这个特定的 class 启动。为此,我像这样实现了 DenormalizerInterface

use App\Service\PostDTO;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

class PostDTODeserializer implements DenormalizerInterface
{
    /** @var ObjectNormalizer */
    private $normalizer;

    /**
     * PostDTODeserializer constructor.
     * @param ObjectNormalizer $normalizer
     */
    public function __construct(ObjectNormalizer $normalizer)
    {
        $this->normalizer = $normalizer;
    }

    public function denormalize($data, string $type, string $format = null, array $context = [])
    {
        return $type === PostDTO::class;
    }

    /**
     * @param mixed $data
     * @param string $type
     * @param string|null $format
     * @return array|bool|object
     * @throws ExceptionInterface
     */
    public function supportsDenormalization($data, string $type, string $format = null)
    {
        // validate the array which will be normalized (you should write your validator and inject it through the constructor)
        if (!is_string($data['name'])) {
            // normally you would throw an exception and leverage the `ErrorController` functionality

            // do something
        }

        // convert the array to the object
        return $this->normalizer->denormalize($data, $type, $format);
    }
}

如果要访问context数组,可以实现DenormalizerAwareInterface。通常,您会创建自定义验证并将其注入到该反规范化器中并验证 $data 数组。

请注意,我在这里注入了ObjectNormalizer,这样当数据成功通过验证时,我仍然可以使用$data.[=22=构造PostDTO ]

PS:在我的例子中,自动装配已经自动注册了我的自定义反规范化器。如果你的没有自动自动装配,请转到 services.yaml 并添加以下行:

 App\Serializer\PostDTODeserializer:
        tags: ['serializer.normalizer']

(我用 serializer.normalizer 标记了实现,以便在反序列化管道中识别它)