Why Getting 400 Bad Request Error When AJAX and PHP Codes are 100% Correct?

Why Getting 400 Bad Request Error When AJAX and PHP Codes are 100% Correct?

Today we are going to talk about a very important topic about WordPress AJAX. Let's deep dive into this problem and try to find out some solutions. I have a lot of experience in my WordPress career related to this problem. Let me share some of them with you here. 

In the previous post, We discussed about how to implement and manage AJAX requests on WordPress with detailed explanations. So, We are not going to talk about those things again here. 

Scenario 

Let's assume that we are going to develop a plugin for WordPress and it has multiple PHP files in multiple directories. For example, if we want to create a form page to collect customer data, then we have to create a new PHP file to include it's HTML code to render the form like this. 

function render() {
        echo '
        <form id="deposit-form" action="#" method="POST" enctype="multipart/form-data">
        <label for="select-option">Select an Option:</label>
        <select id="select-option" required>
            <option value="" disabled selected>Select an option</option>
            <option value="option1">Option 1 - Description 1</option>
            <option value="option2">Option 2 - Description 2</option>
        </select>
        <p id="option-description"></p>
        
        <label for="amount-input">Deposit Amount (USD):</label>
        <input id="amount-input" type="text" value="1000" required><br/>
        
        <div id="verification-wrap">
            <label for="verification">Deposit Slip/Screenshot:</label><br/>
            <input name="verification" id="verification" type="file" required>
        </div>
        <div id="pof-wrap" style="display: none;">
            <label for="pof">Proof of Funds:</label><br/>
            <input name="pof" id="pof" type="file" required>
        </div>
        <input type="submit" id="submit" value="Submit">
        </form>';
        
 }

Here we have one multiple choice option, one text input and two file inputs. For the form submit action, Let's try this jQuery code. 

jQuery(document).ready(function($) {
    $('#deposit-form').submit(function(event) {
      event.preventDefault();
      // Create a FormData object
      var formData = new FormData();
      // Append data to the FormData object
      formData.append('action', 'process_deposit_form');
      formData.append('select_option', $('#select-option').val());
      formData.append('amount_input', $('#amount-input').val());
      formData.append('verification', $('#verification')[0].files[0]);
      formData.append('pof', $('#pof')[0].files[0]);
      $.ajax({
          type: 'POST',
          url: ajax_object.ajaxurl,
          data: formData,
          processData: false,  // To send files
          contentType: false,  // To send files
          success: function(response) {
              if (response.success) {
                  console.log('Form submitted successfully');
                  // You can redirect or perform other actions on success
              } else {
                  console.error('Response Success but Error submitting form', response.data);
              }
          },
          error: function(error) {
              console.error('Response Error and Error submitting form', error);
          }
      });
    });
});

Here, all of the form data will append to one array called formData and send them back to the server via AJAX POST request. Next we need to enqueue this script and localize the AJAX URL. Also, if the plugin not detect jQuery, we need to enqueue it too. (optional) 

wp_enqueue_script('jquery');
wp_register_script('qti-js', plugin_dir_url( __FILE__ ) . '../assets/js/new-entry.js', array( 'jquery' ), null, true );
wp_localize_script('qti-js', 'ajax_object', array('ajaxurl' => admin_url('admin-ajax.php')));
wp_enqueue_script('qti-js');

Let's create the PHP callback function. 

function process_deposit_callback() {
    // Your form processing logic here
    global $wpdb;
    $table_name = $wpdb->prefix . 'deposits';
    $selected_option = sanitize_text_field($_POST['select_option']);
    $amount = sanitize_text_field($_POST['amount_input']);
    // Handle file uploads
    $verification_filename = 'none';
    $pof_filename = 'none';
    if (isset($_FILES['verification']) && $_FILES['verification']['error'] == 0) {
        $verification_filename = sanitize_file_name($_FILES['verification']['name']);
        $upload_dir = wp_upload_dir(); // Get the WordPress upload directory information
        $upload_path = $upload_dir['path'] . '/user-data'; // Include year and month in the path
    
        wp_mkdir_p($upload_path);
        // Append a unique identifier to the filename to avoid overwriting
        $verification_filename = wp_unique_filename($upload_path, $verification_filename);
    
        move_uploaded_file($_FILES['verification']['tmp_name'], $upload_path . '/' . $verification_filename);
    }
    
    if (isset($_FILES['pof']) && $_FILES['pof']['error'] == 0) {
        $pof_filename = sanitize_file_name($_FILES['pof']['name']);
        $upload_path = $upload_dir['path'] . '/user-data';
    
        wp_mkdir_p($upload_path);
        $pof_filename = wp_unique_filename($upload_path, $pof_filename);
    
        move_uploaded_file($_FILES['pof']['tmp_name'], $upload_path . '/' . $pof_filename);
    }
    // Insert data into the database
    $wpdb->insert(
        $table_name,
        array(
            'deposit_type' => $selected_option,
            'deposit_amount' => $amount,
            'deposit_slip' => date('Y/m').'/user-data/'.$verification_filename,
            'deposit_proof' => date('Y/m').'/user-data/'.$pof_filename,
            'status' => 'pending',
            'user_id' => get_current_user_id(),
        ),
        array('%s', '%s', '%s', '%s', '%s', '%d') // Adjust the placeholders based on your data types
    );
    if ($wpdb->last_error) {
        // Something went wrong with the database insertion
        wp_send_json_error('Error submitting form message from PHP');
    } else {
        // Database insertion successful
        wp_send_json_success('Form submitted successfully message from PHP');
    }
    wp_die();
}
add_action('wp_ajax_process_deposit_form', 'process_deposit_callback');
add_action('wp_ajax_nopriv_process_deposit_form', 'process_deposit_callback');

It's very important to make action name in jQuery code exactly similar to add_action with wp_ajax. Also, wp_ajax_nopriv action added for the guest users. Otherwise, not-logged in users will unable to use the form. 

Now, we are coming to the real question. These codes are 100% correct but we have a question. Where we need to put this callback function?

If we put this callback function in the same PHP file that HTML form included, we will get a 400 bad request error. Also, if we put this code in a separate file and include it in the PHP file that has the HTML form code, result will be the same. 

Solution 

If we continously get the 400 bad request error, the simplest solution is, putting this code in the plugin main file. If you are working with a theme, just use functions.php as the main file. The you will not get the error again if your codes are 100% accurate. 

Now there is another problem. If we have multiple callback functions for multiple actions, all of them need to put in the plugin main file and it will be very complex. We have a solution for that. 

  • Create a new file for the callback action and place the function there. 
  • You can also put the add_action, enqueue and localization codes in this same file. 
  • Then include the file in the proper section of plugin main PHP file or functions.php file in the theme. 

That's it. Problem solved. See you again soon with a new article. 



Comments