【问题标题】:How to properly do a REST API POST call using FOSRest and Symfony 3.0如何使用 FOSRest 和 Symfony 3.0 正确执行 REST API POST 调用
【发布时间】:2016-01-06 22:27:40
【问题描述】:

对于我目前正在构建的 API,我希望能够发送带有以下内容的 JSON 正文的请求

{"title": "foo"}

为名为@9​​87654322@ 的实体创建新的数据库记录。

我制作了一个子类FOSRestController 的控制器。为了创建一个项目,我做了一个动作

/**
 * @Route("/")
 *
 * @ApiDoc(
 *     section="Project",
 *     resource=true,
 *     input={"class"="AppBundle\Form\API\ProjectType"},
 *     description="Creates a new project",
 *     statusCodes={
 *         201="Returned when successful",
 *      }
 * )
 *
 * @Method("POST")
 * @Rest\View(statusCode=201)
 */
public function createProjectAction(Request $request)
{
    $project = new Project();
    $form = $this->createForm(ProjectType::class, $project);
    $form->submit(($request->request->get($form->getName())));

    if ($form->isSubmitted() && $form->isValid()) {
        return $project;
    }

    return View::create($form, 400);
}

ProjectType 看起来像这样

class ProjectType extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('title');
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Project'
        ));
    }
}

但是,当我尝试将所述 JSON 发布到 API 时,它会响应 title 属性不能为空,这很好,因为这是为它设置的验证规则。但是,它已设置。我突然意识到我必须发送以实际对象名称为前缀的 JSON 才能使这项工作:

{"project":{"title": "bla"}}

公平地说,这感觉有点奇怪,只需发布​​属性就足够了。

所以,根据这些信息,我只有两个问题:

  1. 为什么我需要用($request->request->get($form->getName()))“提交”这个表单,$request 应该不够吗?
  2. 我需要对 FormType 进行哪些更改才能按原样验证实体,而不是为其添加实体名称的前缀?

编辑 1:在默认选项中添加或删除 data_class 根本不会改变行为。

【问题讨论】:

  • 您是否尝试过使用标准格式提交表单,$form->handleRequest($request);
  • @JasonRoman 是的,但这似乎根本不起作用。无论我发送什么,我什至都无法通过 isValid()。

标签: php rest fosrestbundle symfony


【解决方案1】:

这是因为 Symfony 控制器“createForm”辅助方法的工作原理。其背后的原因是多个表单可能具有相同的目标 URL。通过在表单名称前加上前缀,Symfony 可以知道提交了哪个表单。

这可以通过查看“createForm”方法实现来看出:

public function createForm($type, $data = null, array $options = array())
{
    return $this->container->get('form.factory')->create($type, $data, $options);
}

如果你不想要这种行为,改变它真的很容易:

public function createProjectAction(Request $request)
{
    $project = new Project();
    $form = $this->get('form.factory')->createNamed(null, new ProjectType(), $project);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        return $project;
    }

    return View::create($form, 400);
}

所以你基本上是在创建一个“无名”表单。由于您正在构建 API,因此将其拉入基本控制器中的 createNamelessForm($type, $data, $options) 辅助方法可能是一个好主意,这样您就不必一直显式地从容器中获取 Form Factory 并使其在眼睛。

评论您的修改

包装键不是由“data_class”选项生成的,而是由表单类型上的“getName()”方法生成的。

【讨论】:

  • 非常感谢您的详细解释。我打算今天晚些时候试试这个。
  • 这确实有效,但是,如果您想让它也适用于 NelmioAPIDocBundle,则必须在 AbstractType subclassconfigureOptions 中将 'allow_extra_fields' 设置为 true。还需要将@ApiDoc注解的input属性设置为input={"class"="AppBundle\Form\API\ProjectType", "name"=""}
  • 正确,“allow_extra_fields”对于大多数 REST API 来说是很好的,因为如果声明了一些未在其中定义的属性,Symfony 表单将被视为无效。至于 ApiDoc 捆绑包,我已经尝试过并且非常喜欢这个概念,但我发现它不适用于我拥有的许多案例,所以我最终从我的项目中删除了它。
猜你喜欢
  • 2017-07-10
  • 1970-01-01
  • 2016-05-04
  • 1970-01-01
  • 1970-01-01
  • 2019-02-16
  • 1970-01-01
  • 2017-05-25
  • 2016-06-10
相关资源
最近更新 更多