如何使用FOSRest和Symfony 3.0正确执行REST API POST调用


How to properly do a REST API POST call using FOSRest and Symfony 3.0

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

{"title": "foo"}

以创建名为CCD_ 1的实体的新数据库记录。

我做了一个控制器,它是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根本不会改变行为。

这是因为Symfony Controller"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()"方法生成的。