Converting API call from PHP and cURL to ColdFusion cfhttp
Asked Answered
A

1

6

I am attempting to code an API call to a online testing company. They have provided a sample call in PHP and cURL that I need to implement in ColdFusion 11 using <CFHTTP>. So far my attempt has failed. The only response I get from their server / API is:

Statuscode = "Connection Failure. Status code unavailable. "

and

ErrorDetail = "I/O Exception: Remote host closed connection during handshake".

If it were working I would get a JSON string detailing computed scores. Note that in the code below I've changed a few values for security reasons, other than that it's the original code. Any suggestions or comments would be much appreciated, thanks.

Here is the ColdFusion/cfhttp code:

<cfoutput>
<cfset sdata = [
    {
        "customerid" = "ACompany",
        "studentid" = "test",
        "form" = "X",
        "age" = "18.10",
        "norms" = "grade",
        "grade" = "2"
    },
    {
        "scores" = [
        {"subtest"="math","score"="34"},
        {"score"="23","subtest"="lang"},
        {"score"="402","subtest"="rcomp"}
        ]
    }

]>
<!--- create JSON string for request --->
<cfset jsdata = serializeJSON(sdata)>
<!--- make the call --->
<cfhttp method="Get" url="https://www.APIwebsite.php" timeout="10" result="varx">
     <cfhttpparam type="header" name="Content-Type" value = "application/json; charset=utf-8"/>
     <cfhttpparam type="body" value = "#jsdata#"/>
     <cfhttpparam type="header" name="Authorization" value="AuthCode"/> 
     <cfhttpparam type="header" name="Content-Length" value = "#len(jsdata)#"/>
</cfhttp>

<!--- show results --->
cfhttp return status code: [#varx.statusCode#]<br> 
cfhttp return fileContent: [#varx.fileContent#]<br>
</cfoutput>

Here is the PHP/cURL code:

<?php
    $data = array
    (
    "customerid" => "ACompany",
    "studentid" => "test",
    "scoringtype" => 2,
    "form" => "X",
    "age" => "18.10",
    "norms" => 'grade',
    "grade" => '2',
    "scores" => array(
        array("subtest" => "math", "score" => "34"),
        array("subtest" => "lang", "score" => "23"),
        array("subtest" => "rcomp", "score" => "402")
    ));

    $url = 'https://www.APIwebsite.php';
    $json_string = json_encode($data);

   $headers = array (
        "Content-Type: application/json; charset=utf-8",
        "Content-Length: " .strlen($json_string),
        "Authorization: AuthCode"
    );

    $channel = curl_init($url);
    curl_setopt($channel, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($channel, CURLOPT_CUSTOMREQUEST, "GET");
    curl_setopt($channel, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($channel, CURLOPT_POSTFIELDS, $json_string);
    curl_setopt($channel, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($channel, CURLOPT_CONNECTTIMEOUT, 10);

    $response = curl_exec($channel); // execute the request
    $statusCode = curl_getInfo($channel, CURLINFO_HTTP_CODE);
    $error = curl_error($channel);
    curl_close($channel);

    http_response_code($statusCode);
    if ( $statusCode != 200 ){
        echo "Status code: {$statusCode} \n".$error;
    } else {
        $data = json_decode($response,true);
        foreach ($data as $key => $value) {
            echo nl2br($key . ': ' . $value . "\n");
        }
    }
?>
Amora answered 25/11, 2019 at 18:40 Comment(8)
It may or may not be relavent to the problem, but the CF Code does not include a scoringtype value.Invoice
Thanks for the suggestion, Dan. I'm not sure what you mean by scoringtype. Does something like that appear in the cURL code or do you think it needs adding to the cf code to be equivalent?Amora
Also your method is wrong, either send as post in the body or as get in the url, right now you are sending as get in the body which doesn't make sense If the api supports post, I'd suggest using post else you might end up with issue about the url being too longAfrica
@Amora He just meant that in the example you posted, in curl you have scoringtype in your payload but not in cfhttpAfrica
What url are you using? It is likely the issue for this particular error.. Is the ssl certificate valid? I see you have ssl verifypeer set to false in curl but cfhttp does not have such featureAfrica
Thanks Tofandel for all your comments. In the curl code I have a curlopt_customrequest, "Get" but the payload in curlopt_postfields, $json_string. They seem to me contradictory, but the person I'm liaising with at the company says it's a GET. So I'm confused. I know I don't have an equivalent cfhttpparam to the verifypeer, do you know what it would be?Amora
I also believe it might be a problem at their server since my call doesn't seem to make it to the API. When I simply put the url into the browser with no data or headers I do get a response from the API. It's an error response, of course, but at least the server didn't block the callAmora
Let us continue this discussion in chat.Africa
A
2

First make sure the url you provided is correct (I know it's for the example but .php is not a valid domain name extension), and that the SSL certificate is valid

If both are correct, you should change the request method to POST for sending json data through the body

According to the http semantics

A client should not generate a body in a GET request. A payload received in a GET request has no defined semantics, cannot alter the meaning or target of the request, and might lead some implementations to reject the request and close the connection because of its potential as a request smuggling attack

There is a charset parameter in cfhttp so you don't need to send it in the header

Here is a code that should work

<cfset sdata = [
    {
        "customerid" = "ACompany",
        "studentid" = "test",
        "form" = "X",
        "age" = "18.10",
        "scoringtype" = 2,
        "norms" = "grade",
        "grade" = "2"
    },
    {
        "scores" = [
            {"subtest"="math","score"="34"},
            {"score"="23","subtest"="lang"},
            {"score"="402","subtest"="rcomp"}

        ]
    }

]>
<!--- create JSON string for request --->
<cfset jsdata = serializeJSON(sdata)>
<!--- make the call --->
<cfhttp method="post" charset="utf-8" url="https://api.website.com/" timeout="10" result="varx">
     <cfhttpparam type="header" name="Content-Type" value="application/json"/>
     <cfhttpparam type="header" name="Authorization" value="AuthCode"/> 
     <cfhttpparam type="header" name="Content-Length" value="#len(jsdata)#"/>
     <cfhttpparam type="body" value="#jsdata#"/>
</cfhttp>

<!--- show results --->
<cfoutput>
cfhttp return status code: [#varx.statusCode#]<br>
cfhttp return fileContent: [#varx.fileContent#]<br>
</cfoutput>
Africa answered 25/11, 2019 at 20:29 Comment(5)
Thanks again for all your effort, much appreciated. I tried the code you suggested, with the same result. I confess however to being pretty ignorant re. SSL certificates. How might I know that's a problem? Maybe I should just educate myself some more about them so a pointer or link? ThanksAmora
If you can access the url with HTTPS without getting a warning about security, then there likely isn't an issue and the ssl verifypeer is not neededAfrica
Thanks. I did some more testing and found that although the API responds directly to a hyperlink in a template or the browser url window (with an error of course), I get a connection failure when attempting the same simple link via cfhttp. Whatever else is wrong with my code, this seems a problem in itself. I've seen posts about this that revolve around security certifications. Any comments or suggestions welcome. Also I want to say this has been very helpful. Even though I've not yet resolved the problem I've received confirmation that my code is at least syntactically correct. ThanksAmora
Did you try with an url like google.com? Just to make sure it's not a problem on your serverAfrica
Thanks, I'm back. Actually I have a server on my own machine for development purposes, non ssl. I then have two remote sites on the same server where the app is used, one for testing, one for production. So I have a test bed to craft an api call and get a response from an ssl server, which I've done. At this point I'm pretty convinced there's some setting or protocol at the target company side that is rejecting my call before it gets processed by their API. There may be other problems but that needs addressing first and I need input from them to proceed.Amora

© 2022 - 2024 — McMap. All rights reserved.