Symfony3.4仅含提交按钮表单出现CSRF错误的技术问询
嘿,我在Symfony 3.4里处理过多提交按钮的表单场景,咱们一步步搞定这个CSRF错误问题。
问题根源分析
你遇到的CSRF错误,大概率是因为表单渲染时漏掉了CSRF Token字段——两个提交按钮本身不会携带CSRF Token,而Symfony默认会强制验证这个Token来防止跨站请求伪造。另外,如果处理请求时的逻辑顺序不对,也可能导致Token验证失败。
完整解决方案
下面是经过验证的代码示例,涵盖表单创建、模板渲染和控制器处理全流程:
1. 创建带双提交按钮的表单类型
首先定义表单类,明确开启CSRF保护(Symfony 3.4默认开启,但显式声明更稳妥),并给两个按钮设置唯一的name:
// src/AppBundle/Form/ProductDeleteFormType.php namespace AppBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\Type\SubmitType; class ProductDeleteFormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('delete', SubmitType::class, [ 'label' => '确认删除', 'attr' => ['class' => 'btn btn-danger'] ]) ->add('back', SubmitType::class, [ 'label' => '返回列表', 'attr' => ['class' => 'btn btn-default'] ]); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'csrf_protection' => true, 'csrf_field_name' => '_token', // 可选:指定Token标识,增强安全性 'csrf_token_id' => 'product_delete_operation', ]); } }
2. 控制器处理逻辑
在控制器中,一定要先调用handleRequest让Symfony自动处理CSRF验证,再判断哪个按钮被点击:
// src/AppBundle/Controller/ProductController.php namespace AppBundle\Controller; use AppBundle\Entity\Product; use AppBundle\Form\ProductDeleteFormType; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; class ProductController extends Controller { public function deleteAction(Request $request, Product $product) { $form = $this->createForm(ProductDeleteFormType::class); $form->handleRequest($request); if ($form->isSubmitted()) { // 先验证表单有效性(包含CSRF Token验证) if ($form->isValid()) { if ($form->get('delete')->isClicked()) { // 执行删除操作 $em = $this->getDoctrine()->getManager(); $em->remove($product); $em->flush(); $this->addFlash('success', '商品已成功删除'); } // 无论点击哪个按钮,最终都返回列表页 return $this->redirectToRoute('product_list'); } else { // CSRF验证失败时的提示 $this->addFlash('error', '请求无效,请重试'); return $this->redirectToRoute('product_list'); } } return $this->render('product/delete.html.twig', [ 'product' => $product, 'form' => $form->createView(), ]); } }
3. Twig模板渲染
关键是要确保Symfony能渲染出隐藏的CSRF Token字段,最简单的方式是使用form_end(form)(它会自动调用form_rest输出所有未渲染的字段,包括Token):
{# app/Resources/views/product/delete.html.twig #} {% extends 'base.html.twig' %} {% block body %} <h2>确认删除商品</h2> <p>你确定要删除商品 <strong>{{ product.name }}</strong> 吗?此操作不可撤销。</p> {{ form_start(form) }} {{ form_widget(form.delete) }} {{ form_widget(form.back) }} {{ form_end(form) }} {% endblock %}
避坑要点
- 绝对不能省略
form_end(form)或form_rest(form):这两个方法会自动输出隐藏的CSRF Token字段,没有它Symfony会直接抛出CSRF错误。 - 必须先调用
$form->handleRequest($request):这个方法是Symfony处理表单数据、验证CSRF Token的核心步骤,跳过它直接判断按钮点击会导致验证失效。 - 不要手动关闭CSRF保护:除非你有特殊业务场景,否则保留默认的CSRF保护是必要的安全措施。
内容的提问来源于stack exchange,提问作者Rikijs
相关产品推荐
相关产品推荐

