• Hi everyone,

    I am new to developing and I am running on a very difficult issue.

    I am doing an AJAX call on wordpress.

    there are 2 very strange things I notice.

    1. whenever I set content-type to application/json;charset=UTF-8 I always get 400 (bad request) error
    2. Even though setting Content-type to application/x-www-form-urlencoded I get status code success 200 in my response there is something I can’t understand nor fix.

    I always get before my JSON response an array of the data I sent from the client to the server.

    here is my client ajax call

    sendApiRequestButton.addEventListener("click", function () {
                var email = emailInput.value;
      
                
                  var xhr = new XMLHttpRequest();
                  console.log('ajax_url:', ajax_object.ajax_url);
                  xhr.open('POST', ajax_object.ajax_url, true);
                  
                  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      
                  xhr.onreadystatechange = function () {
                    if (xhr.readyState === XMLHttpRequest.DONE) {
        
                      console.log(xhr.responseText)
                      
                    }
                  };
                  var requestData = 'action=send_api_request&data=' + encodeURIComponent(email);
      
                xhr.send(requestData)
                } 
        );

    here my server-side ajax action

    function send_api_request() {
        
        $email = sanitize_email($_POST['data']);
    
        $api_endpoint = '***';
        $api_key = '***';
    
        $request_data = array(
            'email' => $email
        );
    
        $response = wp_safe_remote_post($api_endpoint, array(
            'headers' => array(
                'Content-Type' => 'application/json',
                'X-Auth-Token' => 'api-key ' . $api_key,
            ),
            'body' => json_encode($request_data),
        ));
    
        if (!is_wp_error($response)) {
    
            $api_response = wp_remote_retrieve_body($response);
            
        } else {
            $api_response = 'API request failed';
        }
    error_log($api_response);
        wp_send_json($api_response);
    }
    add_action('wp_ajax_send_api_request', 'send_api_request');
    add_action('wp_ajax_nopriv_send_api_request', 'send_api_request');
    
    // Add this code to your theme's functions.php or a custom plugin
    function enqueue_get_response_script() {
        wp_enqueue_script('get-response-js', plugins_url('js/get-response-api.js', __FILE__), array(), null, true);
        wp_localize_script('get-response-js', 'ajax_object', array(
            'ajax_url' => admin_url('admin-ajax.php'),
        ));
    }
    add_action('wp_enqueue_scripts', 'enqueue_get_response_script');

    the server receive fine the request with the email field.

    and generate a correct JSON response {message: success} that I checked logging the JSON response right before calling wp_send_json

    anyway something very strange is happening at line wp_send_json($api_response);

    since on my client I receive the JSON message but ALWAYS NO MATTER WHAT preceded by my request data (email) as Array before the JSON as such

    “Array
    (
    [email] => [email protected]
    )
    {message: success}”

    that means that if I try to JSON.parse the response it fails.

    I could manipulate the response to get just the JSON but there is something very wrong here I’d like to understand.

    why my response has always my data request as array?

    and why from the client all request with content-type application/json return 400?

    I am developing with Local from Flywheel with nginx web-server PHP version 8.1.9 WordPress version 6.3 on windows 11

    I tried to deactivate all my plugins (except mine that I am working on)

    I re-installed WordPress

    and I am left with trying to get help from you, please, I really don’t understand this.

    Thank you

Viewing 6 replies - 1 through 6 (of 6 total)
  • Moderator bcworkz

    (@bcworkz)

    There could be a portion of the remote API’s response body that cannot be properly parsed as JSON. What does the result of error_log($api_response); look like?

    The reason content type JSON doesn’t work in your Ajax request is you declare “action” query var with action=send_api_request. When parsed as JSON, it becomes something PHP doesn’t recognize and the construction of WP action hook ‘wp_ajax_send_api_request’ fails to happen as expected.

    For proper parsing as JSON it needs to be declared as an object property

    {action: 'send_api_request',
     data: '[email protected]'}
    Thread Starter francescopanco

    (@francescopanco)

    thanks for that. It is indeed a portion of the remote API’s response body. Now the point is to understand why and then fix it and I have no clue.

    As I said before error_log($api_response) is {message: success} so the correct JSON response. It’s just after wp_send_json that the portion of remote API’s response is appended.

    I am aware that with application/json I need to pass the action inside of a json like that {action: ‘send_api_request’, data: ‘[email protected]’}

    I did everything as you said but it’s not working! always status 400 and response 0 so I turned to

    application/x-www-form-urlencoded
    Moderator bcworkz

    (@bcworkz)

    TBH I don’t have much experience with XHR, I usually rely upon jQuery where encoding an object as JSON works there. If form-urlencoded works for you, then there’s little point in doing something else.

    If {message: success} is all that’s error logged, it doesn’t make sense that the console log is

    Array
    (
    [email] => [email protected]
    )
    {message: success}

    I suspect the response body is itself an array or object where error_log() is expecting a string. Thus error logging doesn’t give you a full picture of the response body. Maybe you should run the response through print_r() before error logging.
    error_log( print_r( $api_response, true ));
    The print_r() return is multi-line. I’m not too sure how well error_log() handles multi-line strings. It might be necessary to write to your own custom log file instead of using error_log().

    Since the Ajax itself is making a successful round trip, the issue must lie in how the API response body is managed server side. You might find it easier to debug your Ajax handler if you took Ajax out of the picture and focused on the PHP handler itself. You could set up a test bed on a custom page template. Assign appropriate values to $_POST, then call send_api_request() directly. You can then insert debug echo and var_dump() lines into the function and immediately see the output right on the test bed page.

    Thread Starter francescopanco

    (@francescopanco)

    I know it doesn’t make sense. But this issue is very similar to this https://stackoverflow.com/questions/40859629/wordpress-ajax-returns-json-with-php-opening-tag even though I don’t have the PHP tag but just an array coming from my request body.

    I did all that and it looks all fine, the API call to the endpoint is all fine, I got the right response, it is just after wp_send_json that the problem comes. I don’t know what to do since the solution for the mentioned link doesn’t work for me. I need to change my local env to see if it is the root cause of that.

    Moderator bcworkz

    (@bcworkz)

    The API response body $api_response that you’re sending as JSON contains more than the data you’re looking for. It’s not just plain text. It might even already be JSON, so perhaps it could just be echoed out and not further JSON encoded with PHP. You either need to break out the data you’re interested in with PHP or do so client side with JavaScript.

    To break out the email with PHP, you need to know how the complete body response is structured. error_log() is not ideal for representing data structures. var_dump() and print_r() are better for this, but their output is not visible when used in an Ajax handler. Their output needs to be sent to a file, or the Ajax handler needs to be called within a conventional HTTP browser response context.

    If you want to manage the API response body client side, you need to JSON.parse() the response so you can properly drill into the data structure to extract the value you’re after.

    Hi,

    I modified the javascript and server response :

    console.log
    "API request failed"

    I think it’s correct why yours

    $api_endpoint = '***';
     $api_key = '***';

    modified javascript with use of FormData:

    <script id="fpAjax">
             document.addEventListener("DOMContentLoaded", function () {
                  sendApiRequestButton.addEventListener("click", function () {
                    if( typeof emailInput == undefined ) return;                   
                       let url = "/wp-admin/admin-ajax.php";   
                       requestData = new FormData;
                       requestData.append( 'action', 'fpAction' );
                       requestData.append( 'data',  emailInput.value );
                       //requestData.append( 'necessary nonce code' );
                  
                       var xhr = new XMLHttpRequest();
        
                       xhr.onreadystatechange = function () {
                           if (xhr.readyState === XMLHttpRequest.DONE) {
    
                              // fpSnipResponse.innerHTML = xhr.responseText;
                               console.log(xhr.responseText);
                         
                           }
                       };
         
                       xhr.open('POST', url, true);
                       xhr.send(requestData)
                   });
       
               });
        </script>

    Modified ajax hooks in php.

    add_action( 'wp_ajax_fpAction', 'send_api_request' );
    add_action( 'wp_ajax_nopriv_fpAction', 'send_api_request');

    I hope this helps. Greetings

Viewing 6 replies - 1 through 6 (of 6 total)
  • The topic ‘AJAX WordPress JSON response always preceded by data request as array’ is closed to new replies.