Yii2 запрос по ajax и ошибка валидации csrf токена Bad Request (#400)
  • 1446

Yii2 ajax и ошибка Bad Request (#400)

Автор: admin | 15 августа (Ср.) 2018г. в 00ч.15м.

В yii2 есть механизм защиты от csrf атак с помощью генирации уникального токена. При работе с формами по средствам виджета ActiveForm токен содержится в скрытом поле и передается на сервер вместе с другими данными из формы.

Если использовать хелпер Html или построить форму на чистом html, то нужно также передавать обработчику данные токена. Иначе будет ошибка Bad Request (#400), которая свидетельствует о провале валидации токена. 

Передача csrf токена по ajax.

Если нужно обработать данные по ajax, то нужно также передать на сервер токен, иначе возникнет такая же ошибка Bad Request и работа скрипта будет остановлена. Мы конечно можем отключить валидацию токена в контроллере или экшене с помощью  $this->enableCsrfValidation = false;
Однако все же в целях безопасности оставить поддержку csrf защиты на сайте.

Итак, допустим у нас есть view.php файл, в котором есть форма. Эту форму мы будем передавать на сервер по ajax. Для примера пусть будет форма комментария:
<?php
use yii\helpers\Html;
use yii\helpers\Url;
use yii\widgets\ActiveForm;
?>

<?php $form = ActiveForm::begin([
        'options' => [
            'id' => $formId,
            'class' => 'comment-form',
         ],
        'action' => Url::toRoute(['/comments//ajax-create']),
]); ?>

<?php echo $form->field($commentFormModel, 'said_name', ['template' => '{input}{error}'])->input('text',['placeholder' => Yii::t('comments/messages', 'Add a name...')]); ?>

<?php echo $form->field($commentFormModel, 'content', ['template' => '{input}{error}'])->textarea(['placeholder' => Yii::t('comments/messages', 'Add a comment...'), 'rows' => 4, 'id' => 'comment-form_content', 'data' => ['comment' => 'content']]); ?>

<?php echo Html::submitButton(Yii::t('comments/messages', 'Comment'), ['class' => 'btn btn-primary comment-submit']); ?>

​

Теперь напишем javascript код, который и будет содержать ajax код:

var $formComment = $('#comment-form');
$( document ).on('submit', '#comment-form', function(e){
    e.preventDefault();
    e.stopImmediatePropagation();
    var form = $(this);
    var $action = $(this).attr( 'action' );
    $.ajax({
            'type' : 'POST',
            'url' : $action,
            'dataType' : 'json',
            'data' : $(this).serialize(),
            'success' : function(data){
            //что-то делаем с результатом
            },
             'error' : function(request, status, error){
                  console.log('error:' + error);
            }
    });
});

В данном случае ошибки Bad Request (#400) не случится, т.к. мы передаем данные из формы, построенной с помощью yii\widgets\ActiveForm. Поэтому csrf токены будут переданы на сервер как нужно.  Но, если нам все же нужно самосьоятельно передать токен по средствам ajax, то делаем следующее. Сначала где-нибудь в коде сохраняем данные токена в переменные таким образом:

$csrf_param = Yii::$app->request->csrfParam; 
$csrf_token = Yii::$app->request->csrfToken;

А затем передаем эти параметры по ajax на сервер вместе с другими данными:

var data = $(this).serializeArray(); //конвертируем данные из формы в массив
data.push({'$csrf_param' : '$csrf_token'});//добавляем токены
$.ajax({
        'type' : 'POST',
        'url' : $action,
        'dataType' : 'json',
        'data' : $.param(data),
        'success' : function(data){
            //что-то делаем с результатом
         },
         'error' : function(request, status, error){
                  console.log('error:' + error);
         }
});

Или передаем имя и значение токена так, если форма в запросе не участвует:

$.ajax({
       'type' : 'POST',
       'url' : '$url',
       'dataType' : 'json',
       'data' : {
                 '$csrf_param' : '$csrf_token',
                 'someDate' : $someDate
        },
//код далее...

Естесственно так как мы передаем переменные из php, то этот код будет размещен внутри php файла. Я обычно для этого использую такую конструцию для размещения js внутри php:

$js = <<<JS

//код

JS;

$this->registerJs($js, \yii\web\View::POS_READY, $id);

Вот в принципе и все. В экшене принимаем данные из POST как обычно и что-то делаем с ними. Проверку на валидность  csrf токена yii2 сделает за нас. Главное мы доставили имя и значение токена с запросом, что и требовалось сделать.

Приветствую!

Меня зовут Сергей. Я - автор этого блога.

Если Вам был полезен материал на моем сайте, поддержите пожалуйста мой проект, чтобы о нем узнали другие люди - кликните plizz :) на иконку в соц. сети, чтобы поделиться материалом с другими.