【问题标题】:Symfony 3 - How to handle JSON request with a formSymfony 3 - 如何使用表单处理 JSON 请求
【发布时间】:2016-01-05 18:42:31
【问题描述】:

我很难弄清楚如何使用 Symfony 表单处理 JSON 请求(使用 v3.0.1)。

这是我的控制器:

/**
 * @Route("/tablet")
 * @Method("POST")
 */
public function tabletAction(Request $request)
{
    $tablet = new Tablet();
    $form = $this->createForm(ApiTabletType::class, $tablet);
    $form->handleRequest($request);

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($tablet);
        $em->flush();
    }

    return new Response('');
}

还有我的表格:

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

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

当我发送一个将 Content-Type 标头正确设置为 application/json 的 POST 请求时,我的表单无效...所有字段均为空。

以下是我在评论if ($form->isValid()) 行时收到的异常消息:

执行“INSERT INTO tablet”时发生异常 (mac_address, site_id) VALUES (?, ?)' with params [null, null]:

我尝试过每次发送不同的 JSON 结果相同:

  • {"id":"9","macAddress":"5E:FF:56:A2:AF:15"}
  • {"api_tablet":{"id":"9","macAddress":"5E:FF:56:A2:AF:15"}}

"api_tablet" 是 getBlockPrefix 返回的内容(Symfony 3 等价于 Symfony 2 中的表单类型 getName 方法)。

谁能告诉我我做错了什么?


更新:

我尝试在我的表单类型中覆盖 getBlockPrefix。表单字段不再有前缀,但仍然没有运气:/

public function getBlockPrefix()
{
    return '';
}

【问题讨论】:

  • 渲染表单并查看元素名称。您会很快看到 macAddress 元素被命名为“form[macAddress]”之类的东西,我自己不使用表单来处理其他内容。我认为命名问题比它的价值更麻烦。但其他人却不这么认为。可能想看看 FOSRestBundle。
  • 我尝试渲染表单,表单名为“api_tablet”,元素为“api_tablet_macAddress”。我尝试使用“api_tablet_macAddress”而不是“macAddress”(有或没有父节点,正如我的问题中提到的那样),但也没有成功。 (我想避免使用 FOSRestBundle,因为我需要的 API 非常小。我宁愿不为这样的基本任务添加包。)
  • 您正在查看 id。元素名称为:name="api_tablet[macAddress]"。将 blockName 设置为 '' 确实会使 api_table 前缀消失,尽管您的表单名称现在也是空白的,这并不是真正可取的。顺便说一句,javascript 有时会遇到 [] 问题,因此请使用浏览器 F12 键来验证实际发布的内容。
  • 你说得对,我的错。我删除了 getBlockPrefix 覆盖并尝试在我的 JSON 中使用括号,但也没有运气。
  • 问题是 JSON 请求(请求主体是 JSON 字符串的请求)的填充方式与“常规”HTTP 请求不同(您可以将 Request 对象转储到看到不同)。因此,您需要首先“解码”请求正文。您可以查看the BodyListener from FOSRestBundle 以获得一些灵感(实现自定义请求处理程序可能也可以,但恕我直言,这并不容易)。

标签: php json symfony


【解决方案1】:
$data = json_decode($request->getContent(), true);
$form->submit($data);

if ($form->isValid()) {
    // and so on…
}

【讨论】:

    【解决方案2】:

    我猜你可以删除表单并填充->验证实体

    $jsonData = $request->getContent();
    $serializer->deserialize($jsonData, Entity::class, 'json', [
        'object_to_populate' => $entity,
    ]);
    $violations = $validator->validate($entity);
    
    if (!$violations->count()) {
    
        return Response('Valid entity');
    }
    
    return new Response('Invalid entity');
    

    https://symfony.com/doc/current/components/serializer.html#deserializing-in-an-existing-object

    https://symfony.com/components/Validator

    【讨论】:

    • 我也在项目中使用验证器类。比使用中间形式更简单。
    • 如果表单有额外的非映射字段,例如agreeTerms,这将不起作用。
    【解决方案3】:

    我建议您使用 FOSRestBundle。

    这是我目前使用的示例配置:

    fos_rest:
        view:
            view_response_listener: force
            force_redirects:
              html: true
            formats:
                jsonp: true
                json: true
                xml: false
                rss: false
            templating_formats:
                html: true
            jsonp_handler: ~
        body_listener: true
        param_fetcher_listener: force
        allowed_methods_listener: true
        access_denied_listener:
            json: true
        format_listener:
            enabled: true
            rules:
                - { path: ^/, priorities: [ 'html', 'json' ], fallback_format: html, prefer_extension: true }
        routing_loader:
                default_format: ~
                include_format: true
        exception:
            enabled: true
            codes:
                'Symfony\Component\Routing\Exception\ResourceNotFoundException': 404
                'Doctrine\ORM\OptimisticLockException': HTTP_CONFLICT
            messages:
                'Symfony\Component\Routing\Exception\ResourceNotFoundException': true
    

    确保你已经用某些东西定义了 getBlockPrefix(我没有尝试过空字符串,所以它可能会起作用):

    public function getBlockPrefix()
    {
       return 'api_tablet'
    }
    

    禁用 CSRF 保护措施:

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'csrf_protection' => false,
            'data_class' => 'AppBundle\Entity\Tablet'
        ]);
    }
    

    然后您可以将以下数据发布到表单:

    {"api_tablet":{"macAddress":"5E:FF:56:A2:AF:15"}}
    

    注意事项/警告:

    • 您需要确保您的表单配置了以下所有字段 实体。您可以在验证器中配置您需要的内容。

    • 您必须在 camelCase 中发帖。 FOSRestBundle 支持数组 标准化,我没有尝试过,但显然这会让 你用下划线发帖。

    【讨论】:

      猜你喜欢
      • 2018-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-27
      • 2014-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多