Home > Software design >  Outputting JSON from Curl APi call to Ajax
Outputting JSON from Curl APi call to Ajax

Time:03-10

I am working on a course project in which I am required to use php to make api calls.

The Ajax call looks like this:

$('#btnOneRun').click(function() {
    $.ajax({
        url: "libs/php/getCapitalSummary.php",
        type: 'POST',
        dataType: 'json',
        success: function(result) {
            if (result.status.name == "ok") {
                console.log(result)
            }
        },
        error: function(jqXHR, textStatus, errorThrown) {
            console.log(errorThrown)
        }
    }); 
});

The php api call looks like this:

<?php

    // remove for production

    ini_set('display_errors', 'On');
    error_reporting(E_ALL);

    $executionStartTime = microtime(true);



    $url='http://api.geonames.org/wikipediaSearchJSON?formatted=true&q=london&maxRows=1&username=flightltd&style=full';

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_URL,$url);

    $result=curl_exec($ch);

    curl_close($ch);

    $decode = json_decode($result, true);   

    $output['status']['code'] = "200";
    $output['status']['name'] = "ok";
    $output['status']['description'] = "success";
    $output['status']['returnedIn'] = intval((microtime(true) - $executionStartTime) * 1000) . " ms";
    $output['data'] = $decode['geonames'];

    
    header('Content-Type: application/json; charset=UTF-8');

    echo json_encode($output); 

?>

This works perfectly. I have used the same routine to make similar calls go the geonames API and had no issues doing so as they provide the name of the root object returned. In the example above, it is called geonames

$output['data'] = $decode['geonames'];

I am trying to use this pattern to make a call to the accuweather API. For this however, I don't have the name of the root object.

I used the routine above, changing that specific line of code to $output['data'] = $result; and voila, I can see where geonames is coming from.

{
    "status": {
        "code": "200",
        "name": "ok",
        "description": "success",
        "returnedIn": "120 ms"
    },
    "data": "{\"geonames\": [{\n  \"summary\": \"London is the capital and most populous city of England and the United Kingdom. Standing on the River Thames, London has been a major settlement for two millennia, its history going back to its founding by the Romans, who named it Londinium (...)\",\n  \"elevation\": 8,\n  \"geoNameId\": 2643743,\n  \"feature\": \"city\",\n  \"lng\": -0.11832,\n  \"countryCode\": \"GB\",\n  \"rank\": 100,\n  \"thumbnailImg\": \"http://www.geonames.org/img/wikipedia/43000/thumb-42715-100.jpg\",\n  \"lang\": \"en\",\n  \"title\": \"London\",\n  \"lat\": 51.50939,\n  \"wikipediaUrl\": \"en.wikipedia.org/wiki/London\"\n}]}"
}

At this point I thought: "Now I just need to do the same with the API call to Accuweather and I will be able to find what I need to type between the curly brackets on $output['data'] = $decode['what_goes_here?']; but when I tried that, the JSON return does not display an object like the one above.

The JSON returned from the accuweather API when called straight from my javascript file, or through the example in their website, looks like this:

[
  {
    "LocalObservationDateTime": "2022-03-10T06:47:00 00:00",
    "EpochTime": 1646894820,
    "WeatherText": "Light rain",
    "WeatherIcon": 12,
    "HasPrecipitation": true,
    "PrecipitationType": "Rain",
    "IsDayTime": true,
    "Temperature": {
      "Metric": {
        "Value": 8,
        "Unit": "C",
        "UnitType": 17
      },
      "Imperial": {
        "Value": 46,
        "Unit": "F",
        "UnitType": 18
      }
    },
    "MobileLink": "http://www.accuweather.com/en/gb/london/ec4a-2/current-weather/328328?lang=en-us",
    "Link": "http://www.accuweather.com/en/gb/london/ec4a-2/current-weather/328328?lang=en-us"
  }
]

I am asking for help with one of two things:

a) A way to decode that JSON object without knowing what that object name is and output that to the AJAX call, or;

b) Receive the decoded object on javascript and decode it to access its properties there.

I immensely thank you in advance.

CodePudding user response:

I carried on looking into it and realized that I could use JSON.parse() in my javascript file.

So I changed that specific line of code in the php file to $output['data'] = $result;

Then on my ajax call I can access the properties of the JSON returned after using calling JSON.parse(result.data) as shown below:

$('#btnOneRun').click(function() {
    $.ajax({
        url: "libs/php/getWeather.php",
        type: 'POST',
        dataType: 'json',
        success: function(result) {
            if (result.status.name == "ok") {
        console.log(JSON.parse(result.data))
            }
        },
        error: function(jqXHR, textStatus, errorThrown) {
            console.log(errorThrown)
        }
    }); 
});

This is logged as:

[
    {
        "LocalObservationDateTime": "2022-03-10T08:13:00 00:00",
        "EpochTime": 1646899980,
        "WeatherText": "Mostly cloudy",
        "WeatherIcon": 6,
        "HasPrecipitation": false,
        "PrecipitationType": null,
        "IsDayTime": true,
        "Temperature": {
            "Metric": {
                "Value": 9.1,
                "Unit": "C",
                "UnitType": 17
            },
            "Imperial": {
                "Value": 48,
                "Unit": "F",
                "UnitType": 18
            }
        },
        "MobileLink": "http://www.accuweather.com/en/gb/london/ec4a-2/current-weather/328328?lang=en-us",
        "Link": "http://www.accuweather.com/en/gb/london/ec4a-2/current-weather/328328?lang=en-us"
    }
]

CodePudding user response:

For starters, don't re-invent the HTTP protocol. Don't send "200" as part of your $output JSON, it already is the HTTP status.

You can use custom HTTP headers to communicate metadata such as how long the request took on the server side. You don't even need to decode the $result, but you really should check for curl errors and the HTTP status code you got from the remote server, and set the proper HTTP status for your own response.

<?php
    $result = curl_exec($ch);

    if (!curl_errno($ch)) {
        header('X-Returned-In: ' . (microtime(true) - $executionStartTime));

        $curl_status = curl_getinfo($http, CURLINFO_HTTP_CODE);
        if (curl_status == 200) {
            header('Content-Type: application/json; charset=UTF-8');    
            echo $result;  // $result already is JSON, there is nothing else to do
        } else {
            header($_SERVER['SERVER_PROTOCOL'] . ' 500 Unexpected API Status', true, 500);
        }
    } else {
        header($_SERVER['SERVER_PROTOCOL'] . ' 500 Curl Failed', true, 500);
    }    
?>

The client should use GET requests unless you actually send new data to the server, which is not the case here.

$('#btnOneRun').click(function() {
    $.ajax({
        type: 'GET',
        url: 'libs/php/getCapitalSummary.php',
        success: function (result) {
            // there is no separate status to check, we *know* it's a success
            console.log(this.getResponseHeader('X-Returned-In'));
            console.log(result);
        }
        error: function (jqXHR, textStatus, errorThrown) {
            // this will log e.g. "Unexpected API Status" or "Curl Failed"
            console.log(textStatus, errorThrown)
        }
    });
});

Regarding your question with the "root object":

$decode = json_decode($result, true);
$output['data'] = $decode['what_goes_here?'];

Nothing goes there. There is no "root object". The entire $decode is an object (or, in AccuWeather's case an array - note the square brackets).

The point is, you don't need to index into it:

$output['data'] = $decode;
  • Related