web-dev-qa-db-ja.com

無効なフォームPOSTデータ-認証済みユーザーのajax

Drupal ajaxを使用したフォームがあります。フォームを送信すると、次のように表示されます。

enter image description here

Drupalログを確認すると、次のエントリがあります。

enter image description here

エラーをGoogleで検索したところ、 this の投稿が見つかりましたが、匿名ユーザーに適用されます。私はこれを認証済みユーザーでのみ使用しています。

コードでエラーを検索すると、ajax_get_form()関数が_ajax.inc_に返されます

_ $form_state = form_state_defaults();

  $form_build_id = $_POST['form_build_id'];

  // Get the form from the cache.
  $form = form_get_cache($form_build_id, $form_state);
  if (!$form) {
    // If $form cannot be loaded from the cache, the form_build_id in $_POST
    // must be invalid, which means that someone performed a POST request onto
    // system/ajax without actually viewing the concerned form in the browser.
    // This is likely a hacking attempt as it never happens under normal
    // circumstances, so we just do nothing.
    watchdog('ajax', 'Invalid form POST data.', array(), WATCHDOG_WARNING);
    drupal_exit();
  }
_

_$form_はオブジェクトではありません。理由はわかりません。

ajax_get_form()関数では、_$form_build_id_のdd()を実行しています。これは_form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kE_のようなものを返します。

データベースで次のSQLを実行すると、結果が得られます。

_select *  from cache_form where cid like '%form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kE%';
_

このインスタンスのcidは_form_state_form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kE_です。

Form_get_cache()のコードは次のとおりです。

_function form_get_cache($form_build_id, &$form_state) {
  if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) {
     //code missing
  }
}
_

cache_get()から_form_form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kE_を探していますが、データベースに格納しているのは_form_state_form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kE_です。もちろん一致しないので何も返されません。

なぜこれが起こっているのかわかりません。

私のフォームコードは次のとおりです

_  $form['welcome'] = array(
    '#markup' => //some html markup,
  );


  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Test'),
    '#ajax' => array(
      'callback' => 'myform_js',
      'wrapper' => 'message_container',
      'method' => 'replace',
      'effect' => 'fade',
      'event' => 'click',
    ),
  );

  $form['result'] = array(
    '#prefix' => '<div id="message_container">',
    '#suffix' => '</div>',
    '#type' => 'fieldset',
  );
_

関数myform_js()は、dd()が先頭に追加されたときに呼び出されないため、ヒットしていません。私は明らかにこれで何か悪いことを見ることができないので、問題を引き起こす可能性のある環境問題があるかどうかわかりません。

_$form['submit']_のタイプをbuttonに変更してみました。

失敗しているのはcache_set()の呼び出しのようです。現時点ではなぜなのかよくわかりません。

6
Rodney

わかった。

_form_form-ymw-BN0Zhp9j2CZf82mZzslbUBiLo8kZbreXhB1y8kE_が設定されていなかったので、form_set_cache()を調べました。

_/**
 * Stores a form in the cache.
 */
function form_set_cache($form_build_id, $form, $form_state) {
  // 6 hours cache life time for forms should be plenty.
  $expire = 21600;

  // Cache form structure.
  if (isset($form)) {
    if ($GLOBALS['user']->uid) {
      $form['#cache_token'] = drupal_get_token();
    }
    cache_set('form_' . $form_build_id, $form, 'cache_form', REQUEST_TIME + $expire);
  }

  // Cache form state.
  if ($data = array_diff_key($form_state, array_flip(form_state_keys_no_cache()))) {
    cache_set('form_state_' . $form_build_id, $data, 'cache_form', REQUEST_TIME + $expire);
  }
}
_

私はcache_set()を掘り下げました

_function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) {
  return _cache_get_object($bin)->set($cid, $data, $expire);
}
_

次にset()を調べました

_  /**
   * Implements DrupalCacheInterface::set().
   */
  function set($cid, $data, $expire = CACHE_PERMANENT) {
    $fields = array(
      'serialized' => 0,
      'created' => REQUEST_TIME,
      'expire' => $expire,
    );
    if (!is_string($data)) {
      $fields['data'] = serialize($data);
      $fields['serialized'] = 1;
    }
    else {
      $fields['data'] = $data;
      $fields['serialized'] = 0;
    }

    try {
      db_merge($this->bin)
        ->key(array('cid' => $cid))
        ->fields($fields)
        ->execute();
    }
    catch (Exception $e) {
      // The database may not be available, so we'll ignore cache_set requests.
    }
  }
_

ここでは例外がキャッチされ、報告されません。 dd()をcatchに追加しました。 drupal_debug.txtを見る

_[message:protected] => SQLSTATE[08S01]: Communication link failure: 1153 Got a packet bigger than 'max_allowed_packet' bytes
_

MySQLの_max_allowed_packet_の値を増やしました。修正されました。

この情報を使って、もう少しグーグルで調べたところ、この post が見つかりました。彼らは私と同じ問題を抱えていました。

9
Rodney

「無効なフォームPOST data」ウォッチドッグメッセージの後に、ajax_get_form()関数がdrupal_exit()を呼び出すことに注意してください。

カスタムモジュールは、これを利用してhook_exit() ...にフックすることで、期限切れ/キャッシュされていないフォームを再構築し、Drupalのajax_commandsフレームワークを使用してフォームを再送信できます。

以下は、「AJAX化された」ユーザーログインフォームでこの問題を解決した方法の例です。

/**
 * Implements hook_exit().
 */
function example_exit($destination = NULL) {
  if (arg(0) == 'system' && arg(1) == 'ajax') {
    $is_user_login_form_submission = isset($_POST) && isset($_POST['name']) && isset($_POST['pass']) && isset($_POST['form_build_id']);

    if ($is_user_login_form_submission) {
      watchdog(__FUNCTION__, 'User login AJAX form submission failed. Trying again...', array(), WATCHDOG_WARNING);

      $form_build_id = $_POST['form_build_id'];
      $form_state = form_state_defaults();
      $form_state['values'] = $_POST; // Important!
      $form = drupal_rebuild_form('user_login', $form_state);
      $form['#build_id_old'] = $form['#build_id']; // Important!

      // Try form submission again after it is rebuilt above
      $commands[] = ajax_command_update_build_id($form);
      $commands[] = ajax_command_invoke('form#user-login', 'trigger', array('submit'));

      print ajax_render($commands);
    }
  }
}
2
Clay

私はこの質問が解決されたことを知っていますが、「無効なフォームPOSTデータ」と遭遇し、原因が異なっていたので、これに遭遇した人のためにそこに捨てます。

基本的に、私の犯人は、特に熱心で、5分ごとにキャッシュをクリアするcronジョブでした。問題はメモリの制限やデータベースへの書き込みではなく、かなり頻繁にフォームがキャッシュされ、キャッシュがクリアされ、キャッシュからフォームを取得しようとすると失敗しました。 cronジョブを時間単位で修正するように切り替える。

1
ognockocaten