所有AJAX请求都返回400错误

时间:2019-03-12 作者:Jesse Orange

我一直在WordPress中深入研究AJAX,并学习了许多教程,但每个请求都会返回400个错误。

在我的functions.php 我添加了以下内容:

Step 1 - 向WordPress注册并添加脚本

/**
 * Initialize the JavaScript we need for loading more
 *
 * @return void
 */
function ajax_load_more_init()
{
    // Register and enqueue the script we need for load more
    wp_register_script(\'ajax-load-more-script\', get_template_directory_uri() . \'/assets/scripts/ajax-load-more.js\', array(\'jquery\'));
    wp_enqueue_script(\'ajax-load-more-script\');

    // Localize the script so we can access tthe variables in PHP
    wp_localize_script(\'ajax-load-more-script\', \'ajax_load_more_object\', array(
        \'ajax_url\' => admin_url(\'admin-ajax.php\'),
    ));
}
Step 2 - 将实际脚本排队

/**
 * Add AJAX loader to scripts
 */
add_action(\'wp_enqueue_scripts\', \'ajax_load_more_init\');
Step 3 - 允许在前端使用AJAX

// AJAX Hook for users
add_action(\'wp_ajax_ajax_load_more\', \'ajax_load_more\');
add_action(\'wp_ajax_nopriv_ajax_load_more\', \'ajax_load_more\');
Step 4 - 一个非常简单的测试函数

/**
 * The backend PHP to actually load more posts
 *
 * @return void
 */
function ajax_load_more()
{
    echo "TESTING";

    wp_die();
}
在实际的AJAX脚本中,我有以下内容:

jQuery(document).ready(function ($) {

  // Initialize Isotope as $grid
  const $grid = $(\'#grid\').isotope({
    itemSelector: \'.grid-item\',
    percentagePosition: true,
    animationEngine: \'best-available\', //CSS3 if browser supports it, jQuery otherwise
    animationOptions: {
      duration: 800,
    },
    masonry: {
      columnWidth: \'.grid-item\',
      gutter: 30,
    },
  })


  var has_run = false;
  var init_offset = 0;

  // Hook into click event
  $(\'button.load-more-posts\').click(function (e) {
    e.preventDefault();

    var button = $(this);
    var nonce = $(this).data("nonce");

    console.log(\'Nonce is: \' + nonce);

    // Disable the button
    button.prop("disabled", true);

    // Check the offset
    if (has_run == false) {
      button.data(\'offset\', $(this).data("offset"));
      init_offset = $(this).data("offset");
    }

    console.log(\'Initial offset is: \' + init_offset);
    console.log(\'Initial offset is: \' + button.data(\'offset\'));

    // Perform AJAX request
    $.ajax({
      type: \'POST\',
      dataType: \'json\',
      url: ajax_load_more_object.ajax_url,
      contentType: \'application/json; charset=utf-8\',
      data: {
        action: \'ajax_load_more\',
        security: nonce,
        init_offset: init_offset,
        offset: button.data(\'offset\'),
      },
      beforeSend: function (xhr) {
        console.log(\'Loading more posts...\')
        button.text(\'Loading\');
      },
      success: function (response) {
        console.log(response);

        // Undo Button Disable
        button.prop("disabled", false);

        // Set Offset
        button.data("offset", offset + 10);

        // Script has run
        has_run = true;

        return false;
      },
      error: function (xhr, status, error) {
        console.log(xhr.responseText);
      }
    });
  });
});
正如您在$ajax 调用,url为admin-ajax 动作是wp_ajax_nopriv_ajax_load_more.

nonce来自前端的按钮,如下所示:

<button class="load-more-posts" data-nonce="<?php echo wp_create_nonce(\'load_more_ajax\'); ?>" data-offset="10">Load More Posts</button>

enter image description here

My working code

因此,由于大家的共同意见,我能够想出一个解决方案,我将在下面详细介绍:

在函数中。php(应该在插件中)

/**
 * Additional thumbnail sizes
 */
add_theme_support(\'post-thumbnails\');
add_image_size(\'post-thumbnail-square\', 300, 300, true);

/**
 * Initialize the JavaScript we need for loading more
 *
 * @return void
 */
function ajax_load_more_init()
{
    // Register and enqueue the script we need for load more
    wp_register_script(\'ajax-load-more-script\', get_template_directory_uri() . \'/assets/scripts/ajax-load-more.js\', array(\'jquery\'));
    wp_enqueue_script(\'ajax-load-more-script\');

    // Localize the script so we can access tthe variables in PHP
    wp_localize_script(\'ajax-load-more-script\', \'ajax_load_more_object\', array(
        \'ajax_url\' => admin_url(\'admin-ajax.php\'),
    ));
}

/**
 * Add AJAX loader to scripts
 */
add_action(\'wp_enqueue_scripts\', \'ajax_load_more_init\');

/**
 * Allow AJAX to be used on the front end
 */
add_action(\'wp_ajax_ajax_load_more_posts\', \'ajax_load_more_posts_callback\');
add_action(\'wp_ajax_nopriv_ajax_load_more_posts\', \'ajax_load_more_posts_callback\');

/**
 * The backend PHP to actually load more posts
 *
 * @return void
 */
function ajax_load_more_posts_callback()
{
    // First check the nonce, if it fails the function will break
    check_ajax_referer(\'ajax_load_more_posts\', \'security\');

    // Get the data we have from the load more button
    $offset = $_POST[\'offset\'];
    $init_offset = $_POST[\'init_offset\'];

    // Get posts with given offset
    if ($offset != null && absint($offset) && $init_offset != null && absint($init_offset)) {
        // Finally, we\'ll set the query arguments and instantiate WP_Query
        $args = array(
            \'post_type\' => \'post\',
            \'posts_per_page\' =>  $init_offset,
            \'offset\' => $offset
        );

        $post_list = array();

        $query = new WP_Query($args);

        if ($query->have_posts()) :
            while ($query->have_posts()) : $query->the_post();

                //$categories = get_the_categories();

                $post_list[] = array(
                    \'category\' => get_the_category(),
                    \'title\' => get_the_title(),
                    \'introduction\' => get_field(\'introduction\'),
                    \'date\' => get_the_date(),
                    \'permalink\' => get_permalink(),
                    \'thumbnail\' => get_the_post_thumbnail(),

                );
            endwhile;
        endif;

        echo json_encode($post_list);

        wp_die();
    }
}
在我的ajax加载程序脚本中

jQuery(document).ready(function ($) {

  var has_run = false;
  var init_offset = 0;

  // Hook into click event
  $(\'button.load-more-posts\').click(function (e) {

    var $grid = $(\'#grid\').isotope({
      itemSelector: \'.grid-item\',
    });

    e.preventDefault();

    var button = $(this);
    var nonce = $(this).data("nonce");

    // Disable the button
    button.prop("disabled", true);

    // Check the offset
    if (has_run == false) {
      button.data(\'offset\', $(this).data("offset"));
      init_offset = $(this).data("offset");
    }

    // Perform AJAX request
    $.ajax({
      type: \'POST\',
      dataType: \'json\',
      url: ajax_load_more_object.ajax_url,
      data: {
        action: \'ajax_load_more_posts\',
        security: nonce,
        init_offset: init_offset,
        offset: button.data(\'offset\'),
      },
      beforeSend: function (xhr) {
        console.log(\'Loading more posts...\')
        button.text(\'Loading\');
      },
      success: function (response) {
        console.log(response);
        button.text(\'Load more\');

        // An array to store new items added via AJAX
        var new_items = [];

        // Run through JSON
        $.each(response, function (key, value) {

          var $new_item = $(`<div class="grid-item article-post-card ${value.category[0][\'slug\']}">
                                <a class="article-post-card__anchor" href=" ${value.permalink}" alt="${value.title}">
                                     <div class="article-post-card__featured-image-container">
                                        <div class="article-post-card__overlay"></div>
                                        <div class="article-post-card__featured-image">
                                            ${value.thumbnail}
                                        </div>

                                        <div class="article-post-card__category-label">
                                            ${value.category[0][\'name\']}
                                        </div>

                                    </div>

                                     <div class="article-post-card__content-wrapper">

                                        <div class="article-post-card__publish-date">
                                            <time class="updated" datetime="">${value.date}</time>
                                        </div>

                                        <div class="article-post-card__title">
                                            ${value.title}
                                        </div>

                                        <div class="article-post-card__excerpt">
                                          ${value.introduction}
                                        </div>

                                    </div>
                                </a>
                            </div>`);

          new_items.push($new_item[0]);
        });

        // Add the new items to the grid
        $grid
          .isotope(\'insert\', new_items)
          .imagesLoaded().progress(function () {
            $grid.isotope(\'layout\');
          });

        // Undo Button Disable
        button.prop("disabled", false);

        // Set Offset
        var offset = button.data("offset");
        button.data("offset", offset + 10);

        // Script has run
        has_run = true;

        return false;
      },
      error: function (xhr, status, error) {
        console.log("There was an error", error);
      }
    });
  });
});
如果你们能提出批评,我想这对其他人也会很有用。

2 个回复
最合适的回答,由SO网友:Sally CJ 整理而成

我没有测试您的代码,但我注意到的一个问题是contentType 您的$.ajax() 电话:

contentType: \'application/json; charset=utf-8\',
因为这样,(从PHP方面)操作(即。ajax_load_more) 在中不可用$_REQUEST[\'action\'] WordPress使用哪个来确定调用的AJAX操作,当操作未知时,WordPress会抛出错误400 Bad Request, 在您的情况下,这将转换为“未知的AJAX操作”。

你应该省略contentType 属性,或者不要将其设置为JSON内容类型字符串

还有,虽然你只是在测试ajax_load_more(), 确保返回正确的JSON响应/字符串,因为dataTypejson.

我希望这对你有帮助。:)

其他注释jQuery.ajax() documentation:

  1. dataType (默认值:Intelligent Guess (xml, json, script, or html))

    您希望从服务器返回的数据类型。如果未指定,jQuery将尝试根据响应的MIME类型推断它

  2. contentType (默认值:\'application/x-www-form-urlencoded; charset=UTF-8\')

    向服务器发送数据时,请使用此内容类型。默认值为“application/x-www-form-urlencoded;charset=UTF-8”,这在大多数情况下都适用。如果将内容类型显式传递给$.ajax(), 然后总是将其发送到服务器(即使没有发送数据)。

SodataType 是来自AJAX响应的内容的内容类型(MIME),而contentType 是要发送给PHP/服务器端函数的内容的内容类型(即ajax_load_more() 在您的情况下)。

更新,以便使用错误的内容类型使标题无法读取?

不,这不是关于标题变得不可读的问题-(请求)标题在PHP中总是可读的。只是request payload (在JSON中)不受WordPress AJAX支持-只有请求主体。但您当然可以将表单字段数据/值作为JSON发送-例如,myField: \'{"foo":"bar","baz":1}\'. 只是不要将内容类型头设置为JSON。

但是,如果您确实必须发送JSON请求负载,而不是标准的URL编码表单数据(即请求正文),那么您可以将AJAX操作附加到AJAX URL,例如。/wp-admin/admin-ajax.php?action=ajax_load_more. 那样你就不会出错400; 然而,这取决于你retrieve/parse JSON请求负载。。

请求有效负载(JSON)

jQuery.ajax({
  type: \'POST\',
  url: \'/wp-admin/admin-ajax.php?action=ajax_load_more\',
  contentType: \'application/json; charset=utf-8\',
  data: {
    action: \'ajax_load_more\',
    foo: \'bar\',
  }
});

请求正文(表单数据)

jQuery.ajax({
  type: \'POST\',
  url: \'/wp-admin/admin-ajax.php\',
  data: {
    action: \'ajax_load_more\',
    foo: \'bar\',
  }
});

SO网友:butalin

您尚未指定任何响应,处理ajax请求的函数必须使用wp\\u send\\u json \\u success()内置函数或其他类似函数返回json编码的数组。

示例:

function ajax_load_more(){

    wp_send_json_success(\'testing...\');

//Don t nee to use die()  wp_send_json success will send json and die

      } 
您可以在wordpress codex, php函数不能回显任何内容,php将数据发送到jquery,然后jquery可以将数据回显到html。