使用 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
标记了实现,以便在反序列化管道中识别它)
我是 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
标记了实现,以便在反序列化管道中识别它)