使用 Symfony/FosRestBundle/JMS Serializer 实现字段白名单的建议

Advice for implementing field whitelists with Symfony/FosRestBundle/JMS Serializer

我目前正在学习如何使用 Symfony 3(带有 FOSRestBundle)和 JMS Serializer 来实现一个相对简单的 API。我最近一直在尝试实现指定功能,作为消费客户端,哪些字段应该在响应中 returned(所请求的实体和关系中的字段)。例如;

尝试实施这种做法明智吗?我最近对此进行了大量研究,但我看不出我可以 1) 限制对单个字段的检索和 2) 只有 return 相关实体,如果它们被明确要求的话。

我对这个概念有过一些初步的尝试,但是即使确保我的存储库仅 return 是 Post 实体(即没有评论),JMS 序列化程序似乎会触发延迟加载所有相关实体,我似乎无法阻止这一点。我已经看到一些 links,例如 this example,但是修复似乎不起作用(例如在 link 中,注释掉的 $object->__load() 调用从未达到无论如何在原始代码中。

我已经实现了一个 relationship-based example of this using JMSSerializer's Group functionality 但不得不这样做感觉很奇怪,当我理想情况下能够构建一个 Doctrine Querybuilder 实例,动态添加 andWhere() 调用并让序列化器只是return 准确的数据,无需加载关系。

很抱歉在这方面乱七八糟,但我已经坚持了一段时间,如果有任何意见,我将不胜感激!谢谢。

您应该能够通过 Groups 排除策略实现您想要的。

例如,您的 Post 实体可能如下所示:

use JMS\Serializer\Annotation as JMS;

/**
 * @JMS\ExclusionPolicy("all")
 */
class Post
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @ORM\Column(type="integer")
     *
     * @JMS\Expose
     * @JMS\Groups({"all", "withFooAssociation", "withoutAssociations"})   
     */
    private $id;

    /**
     * @ORM\Column(type="string")
     *
     * @JMS\Expose
     * @JMS\Groups({"all", "withFooAssociation", "withoutAssociations"})
     */
    private $title;

    /**
     * @JMS\Expose
     * @JMS\Groups({"all", "withFooAssociation"})
     *
     * @ORM\OneToMany(targetEntity="Foo", mappedBy="post")
     */
    private $foos;
}

像这样,如果您的控制器操作 returns a View 使用 serializerGroups={"all"},响应将包含您实体的所有字段。

如果它使用 serializerGroups={"withFooAssociation"},响应将包含 foos[] 关联条目及其 exposed 字段。

并且,如果它使用 serializerGroups={"withoutAssociation"}foos 关联将被序列化程序排除,因此不会被渲染。

要从关联的目标实体(Foo实体)中排除属性,请在目标实体属性上使用相同的Groups以获得链式序列化策略。

当序列化结构良好时,您可以在控制器中动态设置 serializerGroups,以便根据 includefields 参数(即 /posts?fields[]=id&fields[]=title).示例:

// PostController::getAction

use JMS\Serializer\SerializationContext;
use JMS\Serializer\SerializerBuilder;

$serializer = SerializerBuilder::create()->build();
$context = SerializationContext::create();
$groups = [];

// Assuming $request contains the "fields" param
$fields = $request->query->get('fields');

// Do this kind of check for all fields in $fields
if (in_array('foos', $fields)) {
    $groups[] = 'withFooAssociation';
}

// Tell the serializer to use the groups previously defined
$context->setGroups($groups);

// Serialize the data
$data = $serializer->serialize($posts, 'json', $context);

// Create the view
$view = View::create()->setData($data);

return $this->handleView($view);

希望我正确理解了你的问题,这足以帮助你。