You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony 6.2注册表单提交数据为空及CSRF令牌异常求助

Symfony 6.2注册表单提交无数据+CSRF令牌问题排查

问题背景

在Symfony 6.2中沿用Symfony 2/3/4的方式构建注册表单,提交后无法获取数据,还曾出现CSRF令牌错误(已临时禁用)。


相关代码

表单类型(SignupType)

class SignupType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('firstName', TextType::class, [
            'required' => true,
        ]);

        $builder->add('lastName', TextType::class, [
            'required' => true,
        ]);

        $builder->add('email', TextType::class, [
            'required' => true,
        ]);

        $builder->add('password', PasswordType::class, [
            'required' => true,
        ]);

        $builder->add('confirmPassword', PasswordType::class, [
            'required' => true,
        ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => SignupModel::class
        ]);
    }
}

数据模型(SignupModel)

class SignupModel
{
    /**
     * @Assert\NotNull(message="First name is required.")
     * @var string
     */
    private string $firstName;

    /**
     * @Assert\NotNull(message="Last name is required.")
     * @var string
     */
    private string $lastName;

    /**
     * @Assert\NotNull(message="Please enter a valid email address.")
     * @var string
     */
    private string $email;

    /**
     * @Assert\NotNull(message="Password is required")
     * @var string
     */
    private string $password;

    /**
     * @Assert\NotNull(message="Confirm password is required")
     * @var string
     */
    private string $confirmPassword;

    public function getFirstName(): string
    {
        return $this->firstName;
    }

    public function setFirstName(string $firstName): void
    {
        $this->firstName = $firstName;
    }

    public function getLastName(): string
    {
        return $this->lastName;
    }

    public function setLastName(string $lastName): void
    {
        $this->lastName = $lastName;
    }

    public function getEmail(): string
    {
        return $this->email;
    }

    public function setEmail(string $email): void
    {
        $this->email = $email;
    }

    public function getPassword(): string
    {
        return $this->password;
    }

    public function setPassword(string $password): void
    {
        $this->password = $password;
    }

    public function getConfirmPassword(): string
    {
        return $this->confirmPassword;
    }

    public function setConfirmPassword(string $confirmPassword): void
    {
        $this->confirmPassword = $confirmPassword;
    }
}

控制器代码

$form = $this->createForm(SignupType::class, new SignupModel());

if ($request->getMethod() !== Request::METHOD_POST) {
    return $this->render("signup.html.twig", [
        'signupForm' => $form->createView()
    ]);
}

$form->handleRequest($request);
if (!$form->isValid() && $form->getErrors()->count()) {
    $this->addFlash('danger', (string)$form->getErrors());

    return $this->redirectToRoute("signup");
}

dump($form->getData()); die;

Twig模板代码

<form class="js-validate needs-validation" action="{{ path('signup') }}" method="post">
    <div class="mb-5">
        <h3 class="card-title">Signup</h3>
    </div>

    {#
    <div class="row">
        <div class="col-sm-6">
            <div class="mb-4">
                {{ form_row(signupForm.firstName) }}
            </div>
        </div>

        <div class="col-sm-6">
            <div class="mb-4">
                {{ form_row(signupForm.lastName) }}
            </div>
        </div>
    </div>

    <div class="mb-4">
        {{ form_row(signupForm.email) }}
    </div>

    <div class="mb-4">
        <div class="input-group-merge">
            {{ form_row(signupForm.password) }}
        </div>
    </div>

    <div class="mb-4">
        {{ form_row(signupForm.confirmPassword) }}
    </div>
    #}

    {#<input type="hidden" id="signup__token" name="signup[_token]" value="{{ csrf_token('authenticate') }}">#}

    {{ form_rest(signupForm) }}

    {#{% include "shared/_captcha.html.twig" %}#}
    {#<input type="hidden" name="_token" value="{{ csrf_token('authenticate') }}" />#}


    <div class="form-check mb-4">
        <label class="form-check-label" for="termsCheckbox">I accept the <a href="#">Terms and Conditions</a></label>
        <input class="form-check-input" type="checkbox" value="" id="termsCheckbox" required>
    </div>

    {% if error is defined %}
        <div class="alert alert-danger">
            {{ error.messageKey|trans(error.messageData, 'security') }}
        </div>
    {% endif %}

    <div class="d-grid gap-4">
        <button name="signup" type="submit" class="btn btn-primary btn-lg">Signup</button>

        <div class="mb-4">
            <p class="card-text text-muted">
               Already have an account?
                <a class="link" href="{{ path('login') }}">Login</a>
            </p>
        </div>
    </div>
    {#{{ form_end(signupForm) }}#}
</form>

问题解决步骤

1. 修复表单字段渲染问题

模板中所有form_row调用都被注释,仅保留form_rest——但form_rest只会渲染未显式输出的字段,注释所有字段后,页面上没有任何输入框,提交自然无数据。

取消字段渲染代码的注释,确保每个表单字段都被正确渲染:

<div class="row">
    <div class="col-sm-6">
        <div class="mb-4">
            {{ form_row(signupForm.firstName) }}
        </div>
    </div>

    <div class="col-sm-6">
        <div class="mb-4">
            {{ form_row(signupForm.lastName) }}
        </div>
    </div>
</div>

<div class="mb-4">
    {{ form_row(signupForm.email) }}
</div>

<div class="mb-4">
    <div class="input-group-merge">
        {{ form_row(signupForm.password) }}
    </div>
</div>

<div class="mb-4">
    {{ form_row(signupForm.confirmPassword) }}
</div>

2. 修复CSRF令牌问题

不要手动注释或添加CSRF令牌代码,Symfony表单组件会自动处理。使用form_end(signupForm)代替手动闭合表单,它会自动渲染CSRF令牌隐藏字段:

  • 删除所有手动添加CSRF令牌的注释代码
  • 取消{{ form_end(signupForm) }}的注释,替换掉手动的</form>标签

修改后的表单结尾:

<div class="d-grid gap-4">
    <button name="signup" type="submit" class="btn btn-primary btn-lg">Signup</button>

    <div class="mb-4">
        <p class="card-text text-muted">
           Already have an account?
            <a class="link" href="{{ path('login') }}">Login</a>
        </p>
    </div>
</div>

{{ form_end(signupForm) }}

3. 优化控制器逻辑

控制器中!$form->isValid() && $form->getErrors()->count()的判断逻辑冗余,isValid()已经包含错误检查,改为:

$form->handleRequest($request);
if (!$form->isValid()) {
    $errors = [];
    foreach ($form->getErrors(true) as $error) {
        $errors[] = $error->getMessage();
    }
    $this->addFlash('danger', implode(', ', $errors));

    return $this->redirectToRoute("signup");
}

4. 验证字段映射

确认SignupModel的属性名与表单字段名完全一致,且所有属性都有对应的getter和setter(当前代码已满足)。


内容的提问来源于stack exchange,提问作者rashidkhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 20:54:59