如何使用 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"}
为名为 Project
的实体创建新的数据库记录。
我制作了一个子类 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'
));
}
}
但是,当我尝试post对API说JSON时,它回应说title
属性不能为空,这是很好,因为这是为它设置的验证规则。然而,它是设置。我突然意识到我必须发送以实际对象名称为前缀的 JSON 才能使这项工作有效:
{"project":{"title": "bla"}}
公平地说,感觉有点奇怪,只 post 属性应该就足够了。
因此,根据这些信息,我有 2 个问题:
- 为什么我需要 "submit" 这个表格
($request->request->get($form->getName()))
,难道 $request
不够吗?
- 我需要更改什么才能使 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);
}
所以您基本上是在创建一个 "nameless" 表单。由于您正在构建一个 API,将其放入基本控制器中的 createNamelessForm($type, $data, $options)
辅助方法可能是个好主意,这样您就不必一直显式地从容器中获取 Form Factory并让眼睛更舒适。
评论您的编辑
包装密钥不是由 "data_class" 选项生成的,而是由您的表单类型上的 "getName()" 方法生成的。
对于我目前正在构建的 API,我希望能够发送具有以下内容的 JSON 正文的请求
{"title": "foo"}
为名为 Project
的实体创建新的数据库记录。
我制作了一个子类 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'
));
}
}
但是,当我尝试post对API说JSON时,它回应说title
属性不能为空,这是很好,因为这是为它设置的验证规则。然而,它是设置。我突然意识到我必须发送以实际对象名称为前缀的 JSON 才能使这项工作有效:
{"project":{"title": "bla"}}
公平地说,感觉有点奇怪,只 post 属性应该就足够了。
因此,根据这些信息,我有 2 个问题:
- 为什么我需要 "submit" 这个表格
($request->request->get($form->getName()))
,难道$request
不够吗? - 我需要更改什么才能使 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);
}
所以您基本上是在创建一个 "nameless" 表单。由于您正在构建一个 API,将其放入基本控制器中的 createNamelessForm($type, $data, $options)
辅助方法可能是个好主意,这样您就不必一直显式地从容器中获取 Form Factory并让眼睛更舒适。
评论您的编辑
包装密钥不是由 "data_class" 选项生成的,而是由您的表单类型上的 "getName()" 方法生成的。