【问题标题】:Get the CSRF token in a test, "CSRF token is invalid" - functional ajax test在测试中获取 CSRF 令牌,“CSRF 令牌无效” - 功能性 ajax 测试
【发布时间】:2026-02-19 12:30:01
【问题描述】:

我正在尝试在 Symfony2 中测试 ajax 请求。我正在编写一个单元测试,它在我的app/logs/test.log 中引发以下错误:

request.CRITICAL: Uncaught PHP Exception Twig_Error_Runtime: 
"Impossible to access an attribute ("0") on a string variable 
("The CSRF token is invalid. Please try to resubmit the form.")
in .../vendor/twig/twig/lib/Twig/Template.php:388

我的代码相当简单。

public function testAjaxJsonResponse()
{
    $form['post']['title'] = 'test title';
    $form['post']['content'] = 'test content';
    $form['post']['_token'] = $client->getContainer()->get('form.csrf_provider')->generateCsrfToken();

    $client->request('POST', '/path/to/ajax/', $form, array(), array(
        'HTTP_X-Requested-With' => 'XMLHttpRequest',
    ));

    $response = $client->getResponse();
    $this->assertSame(200, $client->getResponse()->getStatusCode());
    $this->assertSame('application/json', $response->headers->get('Content-Type'));
}

问题似乎是CSRF 令牌,我可以为测试禁用它,但我真的不想这样做,我通过发出 2 个请求让它工作(第一个使用表单,我们抓取_token 并使用XMLHttpRequest 发出第二个请求) - 这显然看起来相当愚蠢和低效!

【问题讨论】:

    标签: ajax symfony csrf


    【解决方案1】:

    解决方案

    我们可以为我们的ajax 请求生成我们自己的CSRF 令牌:

    $client->getContainer()->get('form.csrf_provider')->generateCsrfToken($intention);
    

    这里的变量$intention 指的是Form Type Options 中设置的数组键。

    添加intention

    在您的Form Type 中,您需要添加intention 键。例如:

    # AcmeBundle\Form\Type\PostType.php
    
    /**
     *  Additional fields (if you want to edit them), the values shown are the default
     * 
     * 'csrf_protection' => true,
     * 'csrf_field_name' => '_token', // This must match in your test
     *
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Acme\AcmeBundle\Entity\Post',
            // a unique key to help generate the secret token
            'intention' => 'post_type',
        ));
    }
    

    Read the documentation

    在您的功能测试中生成 CSRF 令牌

    现在我们有了intention,我们可以在单元测试中使用它来生成有效的CSRF 令牌。

    /**
     * Test Ajax JSON Response with CSRF Token
     * Example uses a `post` entity
     *
     * The PHP code returns `return new JsonResponse(true, 200);`
     */
    public function testAjaxJsonResponse()
    {
        // Form fields (make sure they pass validation!)
        $form['post']['title'] = 'test title';
        $form['post']['content'] = 'test content';
    
        // Create our CSRF token - with $intention = `post_type`
        $csrfToken = $client->getContainer()->get('form.csrf_provider')->generateCsrfToken('post_type');
        $form['post']['_token'] = $csrfToken; // Add it to your `csrf_field_name`
    
        // Simulate the ajax request
        $client->request('POST', '/path/to/ajax/', $form, array(), array(
            'HTTP_X-Requested-With' => 'XMLHttpRequest',
        ));
    
        // Test we get a valid JSON response
        $response = $client->getResponse();
        $this->assertSame(200, $client->getResponse()->getStatusCode());
        $this->assertSame('application/json', $response->headers->get('Content-Type'));
    
        // Assert the content
        $this->assertEquals('true', $response->getContent());
        $this->assertNotEmpty($client->getResponse()->getContent());
    }
    

    【讨论】:

    • 请注意,从 Symfony 2.3 开始,服务名称是 security.csrf.token_manager 而不是 form.csrf_provider