没有通过验证 - 没有类的Symfony2表单

时间:2014-04-17 02:51:19

标签: php forms validation symfony

彻底搜索后我没有看到问题 我有一个表单即时提交,它不是一个对象,它只是一个电子邮件形式。我想验证数据。根据文档,替代方法是这样做。

use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;

$builder
   ->add('firstName', 'text', array(
       'constraints' => new Length(array('min' => 3)),
   ))
   ->add('lastName', 'text', array(
       'constraints' => array(
           new NotBlank(),
           new Length(array('min' => 3)),
       ),
   ))
;

现在已经完成了,但是我不能以任何形式或形式(双关语)来调用$form->isValid,因为即使它通过了约束违规,看似不可见的东西仍会导致它返回无效。

我觉得我可能需要首先从帖子中提取表单,并通过isValid()传递,但我无法确定。

继承我的方法代码

/**
 * @Route("/share", name="email_share")
 * @Method({"POST"})
 * @return \Symfony\Component\HttpFoundation\Response
 */
public function shareAction( Request $request, array $lessons){

    if(!$lessons || !is_array($lessons))
    {
        throw new HttpException(404, "Whoops! shareAction didnt get the lessons");
    }

    //set up our form defaults here
    $defaultData = array('comment' => 'Type your comment here');

    //build the form here
    $form = $this->createFormBuilder($defaultData)
        ->setAction($this->generateUrl('email_share'))
        ->add("emails", 'email', array(
            'label' => "Recipient's email address (separate with a comma)",
            'constraints' => array(
                new Length(array('min' => 6, 'max' => 2040)), 
                new NotBlank(),
            ),
        ))
        ->add('comment', 'textarea', array(
            'label' => "Leave a comment",

        ))
        ->add('name', 'text', array(
            'label' => "Your name",
            'constraints' => array(
                new Length(array('min' => 3), 'max' => 254)),
                new NotBlank(),
            ),
        ))
        ->add('email', 'email', array(
            'label' => "Your email address",
            'constraints' => array(
                new Length(array('min' => 6, 'max' => 254)),
                new NotBlank(),
            ),
        ))
        ->add('copy', 'checkbox', array(
            'label' => "Send me a copy",
            'required' => false,
        ))
        ->add('cancel', 'submit', array(
            'label' => "Cancel",
        ))
        ->add('save', 'submit', array(
            'label' => "Email Resources",
        ))
        ->getForm();

    if ($this->getRequest()->isMethod('POST')) {

        //data is already validated by constraints added when the form was created since we are not attaching this particular form to any object
        $form->handleRequest($request);

        //alternatively (makes no differene from the former)
        //$form->submit($request->request->get($form->getName()));

        if ($form->isValid()) 
        {
            //have YET to see this
            echo 'valid';
            exit;
        }
        else{

            //echo 'not fuckin valie, WHY?';
            //exit;

            // get a ConstraintViolationList
            $errors = $this->get('validator')->validate( $form );

            $result = '';

            //nothing returns here when form is valid against constraints, its just empty
            echo $errors;

            // iterate on it
            foreach( $errors as $error )
            {
                $error->getPropertyPath() : the field that caused the error
                $error->getMessage() : the error message

            }
        }

        $data = $form->getData();

        return $this->emailUser($data);
    }

    return $this->render('ResourceBundle:Default:resources.html.twig', array(
        'form' => $form->createView(),
    ));
}

这是我发布数据的方式

function postForm($form, callback) {

    /*
     * Get all form values
     */
    var values = {};
    $.each($form.serializeArray(), function (i, field) {
        values[field.name] = field.value;
    });

    /*
     * Throw the form values to the server!
     */
    $.ajax({
        type: 'POST',
        url: '/share',
        data: values,
        success: function (data) {

            callback(data);
        }
    });
}

$(document).ready(function () {
    //bind an event to submit on 'email resources' button
    $('div#share form').submit(function (e) {

        //disable symfonys default submit button hehaviour
        e.preventDefault();

        postForm($(this), function (response) {
            //replace html here
            // Is this where im going wrong? Do i need to replace the form here?
        });
    });
});

编辑:以下是主要模板代码的相关部分,它首先调用操作

<div id="share" class="hidden" >
    <h2>Share Resources</h2>
    {% render url('email_share') %}
</div>

这里是在shareAction中呈现的表单模板代码(当前是完整的)

{{ form(form) }}

{% if form | default %}
    {{ form(form) }}
{% endif %}
{% if mail_response | default %}
    {{ dump(mail_response) }}
{% endif %}

表单的隐藏令牌输入部分

<input id="form__token" class="form-control" type="hidden" value="8QWLo8xaPZFCKHBJbuc6CGNIcfmpWyT-yFdWScrsiJs" name="form[_token]">

这两个下划线让我有点担忧(form__token)

EDIT2: 问题在于某处的CSRF令牌。它可以是表单输入名称,令牌本身已经过期,或其他东西。

我通过构建我自己的表单模块

来缩小范围
    //set up our form defaults here
    $defaultData = array('comment' => 'Type your comment here');

    $session = new Session();
    $secret = '123xyz';
    $vendorDir = realpath(__DIR__ . '/../vendor');
    $vendorFormDir = $vendorDir . '/symfony/form/Symfony/Component/Form';
    $vendorValidatorDir =
    $vendorDir . '/symfony/validator/Symfony/Component/Validator';

    // create the validator - details will vary
    $validator = Validation::createValidator();

    $formFactory = Forms::createFormFactoryBuilder()
        ->addExtension(new HttpFoundationExtension())
        //->addExtension(new CsrfExtension(new SessionCsrfProvider($session, $secret)))
        ->addExtension(new ValidatorExtension($validator))
        ->getFormFactory();

    //build the form here
    $form = $formFactory->createBuilder('form', $defaultData)
        ->setAction($this->generateUrl('email_share'))
        ->setMethod('POST')
        ->add("emails", 'email', array(
    //......
    //same as above for the rest......

表单FINALLY会像这样通过验证,当我取消注释该行时      - &gt; addExtension(new CsrfExtension(new SessionCsrfProvider($ session,$ secret))) 我得到了与之前相同的错误,即CSRF令牌无效。

对我而言,这几乎指向了这个模块中的某个地方,或者我没有调用某些东西,或者正确扩展,或者javascript返回的表单比CSRF模块预期的要早,或者隐藏的令牌形式input的名称不是CSRF模块正在查找的名称。我不太了解symfony的内部故障来诊断这个,这就是我来这里寻求帮助的原因。有人看到了潜在的问题吗?

编辑:3 我觉得我不应该使用isValid(),如上所述,我没有传递一个对象,我传递一个数组。请参阅此网址http://symfony.com/doc/current/book/validation.html#validating-values-and-arrays。我试图弄清楚如何正确检查约束,我认为isValid()毕竟不是去的方式,否则我错过了一些基本的东西..我只是无法想象如果我只检查约束错误,怎么能我仍然使用CSRFprotection,还是只用于对象或东西?我是否需要手动传递此信息,因为我不使用对象?

编辑4: 看起来我可能已经发现问题的关键,但我还无法弄清楚如何解决它。

在此文件上Symfony \ Component \ Form \ Extension \ Csrf \ CsrfProvider \ DefaultCsrfProvider 我放了一些输出来跟踪令牌,似乎令牌正在重新生成以进行比较,这似乎不应该是我希望它会将传递的令牌与内存中的令牌进行比较,但事实上,它被生成两次,一次为表格,然后再次进行比较。

起初我怀疑浏览器和ajax可能是从两个不同的会话运行的,并且可能由于我使用SessionCsrfProvider()而导致不匹配,但切换到 - &gt; addExtension(新的CsrfExtension(新的DefaultCsrfProvider($ secret)))我遇到了同样的问题。

这是一个错误,我是否疯了,或者我错过了一些简单的形式或形式的形式ID?

下面是代码,以及我从该代码中找到的结果。

//Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider

public function isCsrfTokenValid($intention, $token)
{

    echo '<pre>Warning, Symfony\Component\Form\Extension\Csrf\CsrfProvider\isCsrfTokenValid';
    echo'<br>, here is out token handed down to compare<br>';
    var_dump($token);
    echo '<br>the new generated token thats being compared to is<br>';
    var_dump($this->generateCsrfToken($intention));
    echo '</pre>';

    return $token === $this->generateCsrfToken($intention);
}

返回

  

//表格   array(6){[&#34;电子邮件&#34;] =&gt; string(19)&#34; email@email.net"
  [&#34;评价&#34;] =&GT;字符串(2)&#34;哟&#34; [&#34;名称&#34;] =&GT; string(5)&#34; me&#34;
  [&#34;电子邮件&#34;] =&GT; string(19)&#34; email@email.net" [&#34;复印&#34;] =&GT;
  字符串(1)&#34; 1&#34; [&#34; _token&#34;] =&GT;串(40)   &#34; a11e10eb323f7a4d19577e6d07e68be951ceb569&#34; }

     

警告,   Symfony \ Component \ Form \ Extension \ Csrf \ CsrfProvider \ isCsrfTokenValid,   这里是令牌传递给比较字符串(40)   &#34; a11e10eb323f7a4d19577e6d07e68be951ceb569&#34;

     

要比较的新生成令牌是字符串(40)   &#34; e83cdf94b15e63e822520b62402eb66e0b1f03d3&#34;

The CSRF token is invalid. Please try to resubmit the form.
     
    

块引用

  

编辑5: 问题已经在这里找到了,看看DefaultCsrfProvider中的这段代码

public function generateCsrfToken($intention)
{
    return sha1($this->secret.$intention.$this->getSessionId());
}
public function isCsrfTokenValid($intention, $token)
{
    return $token === $this->generateCsrfToken($intention);
}

在ajax调用期间,令牌永远不会有效,除非在generateCsrfToken()令牌方法中设置了一个参数,以允许传递会话,您希望通过ajax传递该会话,如此

public function generateCsrfToken($intention, $session)
{
    if(!$session)
    { 
        $session = $this->getSessionId()
    }
        return sha1($this->secret.$intention.$session);
}

我认为首先会完全降低CSRF整体理念的安全性。

是否有另一个提供,我可以在symfony框架内用于ajax调用?如果依赖于sesion,这几乎不会遗漏SessionCsrfProvider类和DefaultCsrfProvider类来处理它,除非我遗漏了一些非常明显的东西......我应该抓住,传递,然后重置ajax调用上的会话? ??
好吧,在我想到这一点之后到目前为止,我刚发现这篇文章Symfony CSRF and Ajax看不清我是否可以从中做出正面或反面。

1 个答案:

答案 0 :(得分:2)

要查看错误,您应该呈现该表单。在表单无效的代码中,该方法返回$ this-&gt; emailUser($ data);但它应该呈现形式。试试这个:

if ($this->getRequest()->isMethod('POST')) {

    //data is already validated by constraints added when the form was created since we are not attaching this particular form to any object
    $form->handleRequest($request);

    //alternatively (makes no differene from the former)
    //$form->submit($request->request->get($form->getName()));

    if ($form->isValid()) 
    {
        //have YET to see this
        echo 'valid';

        //this is the place to process data 
        //$data = $form->getData();

        //return $this->emailUser($data);

        exit;
    }

}

return $this->render('ResourceBundle:Default:resources.html.twig', array(
    'form' => $form->createView(),
));

这应该在提交和显示错误时再次呈现无效表单

你也可以尝试这个

    if ($form->isValid()) 
    {
        //have YET to see this
        echo 'valid';
        exit;
    }else{
        echo '<pre>';
        \Doctrine\Common\Util\Debug::dump($form->getErrorsAsString(), 9);
        echo '</pre>';
    }

如果表单无效,则显示调试样式中的错误。