Symfony 5 Serializer - 如何不公开整个数据库

Symfony 5 Serializer - How to not expose whole database

我们使用 Symfony Serializer 将一些 Doctrine 对象转换为 JSON,目标是将它们作为 API 调用的结果提供。

Out 数据模型有大约三十个 classes,所有这些都以某种方式 link 相互关联,Doctrine 模型反映了这一点。因此从一个实例导航到其他 linked 实例很容易。

现在,我们很高兴向 class 添加新属性时无需进行任何更改。然而,情况不同的是添加了一个新的 link,这通常会增加太多信息,因为 linked classes 也有 links 并且它们有 links 和对象数组...

为了限制这一点,我们通常添加忽略的属性:

protected function serialize($e, $ignored = ['user'])
{
    if ($this->container->has('serializer')) {
        $this->serializer = $this->container->get('serializer');
    } else {
        throw new ServiceNotFoundException('Serializer not found');
    }

    return $this->serializer->serialize($e, 'json', [
        'ignored_attributes' => $ignored,
        ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object) {
            if (method_exists($object, 'getUuid')) {
                return $object->getUuid();
            } elseif (method_exists($object, 'getId')) {
                return $object->getId();
            } elseif (method_exists($object, '__toString')) {
                return $object->__toString();
            } else {
                return '[Reference to object of type ' . get_class($object) . ']';
            }
        },
    ]);
}

然后:

return new Response($this->serialize(
    [
        'presentation' => $presentation,
    ],
    [
        'zoomUser',
        'userExcelDownloads',
        'presentationUserTopics',
        'addedBy',
        'user',
        'presentations',
        'sponsorScopes',
        'sponsorPresentations',
        'showroomScope',
        'presentationsForTopic',
        'presentationsForTopics',
        'presentationHistories',
        'showroomTopics',
        'presentation',
        'presentationHistory',
    ]
));

这行得通,但维护起来很糟糕 - 每当更改数据库模型时,都会存在 API 调用发出更多 MB 的风险,因为必须忽略新属性。没有办法找到这个。

那你是怎么处理的呢?

一种解决方案可能是限制类似于 CIRCULAR_REFERENCE_HANDLER 的序列化深度,即对于第三层的对象,只需添加 ID 而不是完整的对象。我该如何构建它?

Symfony 序列化器有内置的忽略策略(https://symfony.com/doc/current/components/serializer.html#ignoring-attributes

您可以直接从模型中忽略该属性。

use Symfony\Component\Serializer\Annotation\Ignore;

class Presentation
{
    /**
     * @Ignore()
     */
    public $zoomUser;


    //...
}

或使用上下文。

use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;

$normalizer = new ObjectNormalizer();
$encoder = new JsonEncoder();

$serializer = new Serializer([$normalizer], [$encoder]);
$serializer->serialize($presentation, 'json', [AbstractNormalizer::IGNORED_ATTRIBUTES => ['zoomUser']]);

我们切换到 JMS Serializer Bundle,其中设置了最大值。深度很简单,对我们帮助很大。

https://jmsyst.com/bundles/JMSSerializerBundle

对于 Symfony 序列化器,唯一的方法是使用序列化组。