How to send a cURL POST without request data in PHP? - php

My goal is to send a POST request to a server and get the proper response.
Note: Angled brackets represent placeholders.
In Terminal, using the following code will provide me the desired response.
curl -u <user>:<pass> -H 'Content-Type: application/xml' -X POST https://<rest of url>
My current PHP looks something like this:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $uri); //$uri is the same that I use in terminal
curl_setopt($ch, CURLOPT_USERPWD,
sprintf('%s:%s', $user, $pass)); //same as terminal user & pass
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$headers = array(
'Content-Type: application/xml', //expect an xml response
);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$curl_result = curl_exec($ch);
Using this PHP, I get a 400 Bad Request error.
The verbose information:
> POST <same url> HTTP/1.1
Authorization: Basic YWRtaW5Ac3Bhcms0NTEuY29tOnNwYXJrc29tZXRoaW5n
Host: <correct host>
Accept: */*
Content-Type: application/xml
Content-Length: -1
Expect: 100-continue
* HTTP 1.0, assume close after body
< HTTP/1.0 400 Bad request
< Cache-Control: no-cache
< Connection: close
< Content-Type: text/html
Why am I getting a 400 Bad Request error when I use PHP, but not when I use command line? How can I fix this issue so that I get my desired response using PHP?

curl_setopt($ch, CURLOPT_POSTFIELDS, array());
After adding this line, I resolved my problem. In a way, this solution makes sense; but I don't understand why CURLOPT_POSTFIELDS is required. In the PHP documentation, this part should be included under CURLOPT_POST, unless this just accidentally works.

I don't know if this can help you, but for me the Expect: 100-continue looks strange. Take a look at this comment:
http://php.net/manual/en/function.curl-setopt.php#82418
So maybe you can fix it like in the example:
<?php
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
?>

Related

Forbidden to Call method on Dynamics 365 AX via PHP Curl

I am trying to call methods from dynamics SOAP through WSDL via PHP curl.
I get this error from both my webapp and SOAPUI.
What could be the problem? It works fine when accessed from a .NET testing program with same credentials. Just facing problems from PHP side saying Forbidden with 1317 code. The specified account does not exist
I've been trying to call the method and faced different issues last issue I faced is this one.
I thought maybe user agent I changed it I used SOAPUI. same thing.
What I know is the user is registered in Azure AD and should have authorization for the app.
The POST is
POST /soap/services/servicemethodname?wsdl
HTTP/1.1
Host: domainname.sandbox.ax.dynamics.com
Accept: text/xml
Accept-Encoding: gzip,deflate
Connection: Keep-Alive
Content-type: text/xml
User-Agent: Apache-HttpClient
Authorization: Bearer longTokenString
Soapaction: "http://tempuri.org/webservice/method"
Content-Length: 795
The Response is
HTTP/1.1 500 Internal Server Error Cache-Control: private
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/10.0
Strict-Transport-Security: max-age=31536000; includeSubDomains
Set-Cookie: ASP.NET_SessionId=hghtgkuhlihkjg; path=/; secure;
HttpOnly Set-Cookie:
ms-dyn-csrftoken= someTokenSTring; path=/; secure
ms-dyn-fqhn:
ms-dyn-namespace: namespace
ms-dyn-tenant: tenantidstring
ms-dyn-role:
ms-dyn-aid: aidString
X-Powered-By: ASP.NET
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
p3p: CP="No P3P policy defined. Read the Microsoft privacy statement at https://go.microsoft.com/fwlink/?LinkId=271135"
Strict-Transport-Security: max-age=31536000;
includeSubDomains Date: Thu, 01 Aug 2019 19:24:52 GMT Content-Length: 1112
a:ForbiddenForbidden1317System.ComponentModel.Win32ExceptionThe specified account does not exist0-2147467259
I need to be able to call the method without errors and get the values it sends.
My php code
$requestBody = trim('<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dat="http://schemas.microsoft.com/dynamics/2013/01/datacontracts" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tem="http://tempuri.org">
<soapenv:Header>
<dat:CallContext>
<dat:Company>company</dat:Company>
<dat:Language>en-us</dat:Language>
<dat:MessageId>?</dat:MessageId>
<dat:PartitionKey>12345667</dat:PartitionKey>
</dat:CallContext>
</soapenv:Header>
<soapenv:Body>
<m:getMethod xmlns:m="http://tempuri.org/webService/getMethod">
<m:parameterName soap:mustUnderstand="1">12345</m:parameterName>
</m:getMethod>
</soapenv:Body>
</soapenv:Envelope>
');
$soapAction = 'SOAPAction: http://tempuri.org/webService/getMethod';
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER,
array( 'Accept:text/xml',
'Accept-Encoding: gzip,deflate',
'Connection: Keep-Alive',
'Content-type: text/xml; charset=utf-8',
'Cache-Control: no-cache',
'Pragma: no-cache',
'Authorization: Bearer longstringToken',
'SOAPAction: http://tempuri.org/webService/getMethod'
));
if ($postData != '') {
curl_setopt($ch, CURLOPT_POSTFIELDS,$postData);
}
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
// By default https does not work for CURL.
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt ($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
// Set the option to recieve the response back as string.
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$odataURL = 'https://domainname.sandbox.ax.dynamics.com/soap/services/webService';
curl_setopt($ch, CURLOPT_URL, $odataURL);
// enable string response
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
curl_setopt($ch, CURLOPT_HEADER, true);
// Mark as Post request
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
// $output contains the output string
$output = curl_exec($ch);
Ok so finally found a solution.
It helps to read documentations on the classes you use and different systems used. In my case i was trying to integrate my app with microsoft dynamics 365 ax, so i had to read up on that too.
I read a lot of documents some were related to different dynamics service but this one helped most
And since the soap service needed Authorization Header, because they were using Windows authentication, we needed to get the token from oAuth link.
https://login.windows.net/$tenantDomainName/oauth2/token
PS: the oauth2 link i knew about it from github PHPConsoleApplication
I used PHP CURL to get my authorization Token and then created a client using PHP's SoapClient Class.
Make sure you add the authorization token in the header like so:
$arrayOpt = array(
'stream_context' => stream_context_create(
array('http' =>'Authorization: Bearer tokenString')
));
$client = new SoapClient($wsdl, $arrayOpt);
$response = $client->serviceMethod($parameters);
var_dump($response);
And you will get the values of the method.

Curl php request error

I'm trying to set up an API call through php using cURL. The API documentation gives an example for a call to this API from Java:
HttpResponse<JsonNode> response = Unirest.get("https://api2445582011268.apicast.io/games/1942?fields=*")
.header("user-key", "*******")
.header("Accept", "application/json")
.asJson();
This is my attempt at converting it to a cURL call in php:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api2445582011268.apicast.io/games/1942?fields=*");
curl_setopt($ch, CURLOPT_HEADER, array(
"user-key: *******",
"Accept: application/json"
));
$output = curl_exec($ch);
echo $output;
curl_close($ch);
However my php is echoing the following output:
HTTP/1.1 403 Forbidden Content-Type: text/plain; charset=us-ascii Date: Sat, 02 Sep 2017 02:52:40 GMT Server: openresty/1.9.15.1 Content-Length: 33 Connection: keep-alive Authentication parameters missing1
Does anyone know how to solve this?
You used CURLOPT_HEADER (which is a Boolean to indicate you want the headers in the output), but you need CURLOPT_HTTPHEADER (which is used to pass an array with the headers for the request):
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"user-key: *******",
"Accept: application/json"
));

Airbnb API - Action not found - Using cURL

I'm trying to connect to Airbnb and return my reservations from it.
I was able to successfully login using cURL, but I can't retrieve the reservations.
The reservations URL: http://airbnbapi.org/#get-host-messages
As you can see the HTTP Method is GET, client_id must be passed in the URL and X-Airbnb-OAuth-Token in the headers (after login).
After perform the cURL on login I receive the following header output (by using the function CURLINFO_HEADER_OUT):
POST /v1/authorize HTTP/1.1
Host: api.airbnb.com
Accept: */*
Content-Length: 511
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------aa1ew132ff32wca9
With the 200 HTTP code.
After login, I perform the cURL on reservations and I get:
GET /v2/threads?client_id=MyApiKey&_limit=10 HTTP/1.1
Host: api.airbnb.com
Accept: */*
{"error_code":404,"error_type":"invalid_action","error_message":"Action not found."}
With the following code:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.airbnb.com/v2/threads?client_id=MyApiKey&_limit=10');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-Airbnb-OAuth-Token: ' . $MyAccessToken));
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
$response = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerout = curl_getinfo($ch, CURLINFO_HEADER_OUT);
curl_close($ch);
print_r($headerout);
return array($http, $response);
If I use the hurl.it with my values it works without any problem.
Solved.
The problem was that my return should be decoding the $response as JSON and instead I'm retrieving it as plain text and therefore I'm not able to get the access_token parameter.
And since error_reporting is turned off, I didn't see that access_token was empty.
return array($http, json_decode($response));

Why is my POST with cURL not returning JSON correctly?

In PHP, I'm trying to retrieve the url for a specific page in DocuSign that constantly refreshes. The POST to retrieve this url is in the form:
POST http://demo.docusign.net/restapi/{apiVersion}/accounts/{accountId}/envelopes/{envelopeId}/views/recipient
This should return a json file in the form:
{
"url": "example.example.com"
}
However, I am extremely new to using PHP and POST methods and don't believe I'm doing this correctly. The API explorer for this method in particular is here. I am using cURL methods to make this request. Here is my code ($recipient,$account_id,$access_token are found accurately within another file):
$url = "http://demo.docusign.net/restapi/v2/accounts/$account_id
/envelopes/$envelope_id/views/recipient";
$body = array("returnUrl" => "http://www.docusign.com/devcenter",
"authenticationMethod" => "None",
"email" => "$recipient",
"userName" => "$recipient");
$body_string = json_encode($body);
$header = array(
'Accept: application/json',
'Content-Type: application/json',
'Content-Length: '.strlen($body_string),
);
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $body_string);
$json_response = curl_exec($curl);
$response = json_decode($json_response, true);
var_dump($response);
I am able to get the correct return on the API explorer, but not when making the request with PHP. I believe this is due to the fact that I am not incorporating the $header or $body correctly, but at this point I am just not sure.
ADDED: This is the raw output for the request when correctly running the method on the API Explorer:
Accept: application/json
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,fa;q=0.6,sv;q=0.4
Cache-Control: no-cache
Origin: https://apiexplorer.docusign.com
Referer: https://apiexplorer.docusign.com/
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Authorization: Bearer fGehcK7fkRvFguyu/7NGh01UUFs=
Content-Length:
Content-Type: application/json
This is the JSON request being formed in my code:
{
"returnUrl":"http:\/\/www.docusign.com\/devcenter",
"authenticationMethod":"Password",
"email":"example#example.com",
"userName":"example#example.com",
"clientUserId":"4c6228f4-fcfe-47f9-bee1-c9d5e6ab6a41",
"userId":"example#example.com"
}
You are not hitting a valid DocuSign URL in your cURL code. Right now you are sending requests to:
http://demo.docusign.net/apiVersion/v2/accounts/{accountId}/envelopes/{envelopeId}/views/recipient
Instead of "apiVersion" it should be "restApi" like this:
http://demo.docusign.net/restapi/v2/accounts/{accountId}/envelopes/{envelopeId}/views/recipient
We can't send post fields, because we want to send JSON, not pretend to be a form (the merits of an API which accepts POST requests with data in form-format is an interesting debate). Instead, we create the correct JSON data, set that as the body of the POST request, and also set the headers correctly so that the server that receives this request will understand what we sent:
$data = array("name" => "Hagrid", "age" => "36");
$data_string = json_encode($data);
$ch = curl_init('http://api.local/rest/users');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string))
);
$result = curl_exec($ch);
All these settings are pretty well explained on the curl_setopt() page, but basically the idea is to set the request to be a POST request, set the json-encoded data to be the body, and then set the correct headers to describe that post body. The CURLOPT_RETURNTRANSFER is purely so that the response from the remote server gets placed in $result rather than echoed. If you're sending JSON data with PHP, I hope this might help!
I know this question was asked more than 3 years ago, but this may help someone who finds this question because they are having the same problem. I do not see a cURL option that will decode the response in your code. I have found that I need to use the cURL option CURLOPT_ENCODING like this: curl_setopt($ch,CURLOPT_ENCODING,""); According to the PHP manual online, it says, 'CURLOPT_ENCODING - The contents of the "Accept-Encoding: " header. This enables decoding of the response. Supported encodings are "identity", "deflate", and "gzip". If an empty string, "", is set, a header containing all supported encoding types is sent.' You can find this option at https://www.php.net/manual/en/function.curl-setopt.php. I hope this helps save someone from having a headache.

How to perform a PUT operation using CURL in PHP?

I would like to perform a PUT operation on a webservice using CURL. Let's assume that:
webservice url: http://stageapi.myprepaid.co.za/api/ConsumerRegisterRequest/cac52674-1711-e311-b4a8-00155d4905d3
municipality= NMBM
sgc= 12345
I've written the code below, but it outputs this error message: "ExceptionMessage":"Object reference not set to an instance of an object.". Any help would be so much appreciated. Thanks!
<?php
function sendJSONRequest($url, $data)
{
$data_string = json_encode($data);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Accept: application/json',
'X-MP-Version: 10072013')
);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
ob_start();
$result = curl_exec($ch);
$info = curl_getinfo($ch);
if ($result === false || $info['http_code'] == 400) {
return $result;
} else {
return $result;
}
ob_end_clean();
curl_close($ch);
}
$mun = $_GET['municipality'];
$sgc = $_GET['sgc'];
$req = $_GET['req']; //cac52674-1711-e311-b4a8-00155d4905d3
//myPrepaid PUT URL
echo $mpurl = "http://stageapi.myprepaid.co.za/api/ConsumerRegisterRequest/$req";
// Set Variables
$data = array("Municipality" => "$mun", "SGC" => "$sgc");
//Get Response
echo $response = sendJSONRequest($mpurl, $data);
?>
I copied your code, but changed it so it pointed at a very basic HTTP server on my localhost. Your code is working correctly, and making the following request:
PUT /api/ConsumerRegisterRequest/cac52674-1711-e311-b4a8-00155d4905d3 HTTP/1.1
Host: localhost:9420
Content-Type: application/json
Accept: application/json
X-MP-Version: 10072013
Content-Length: 37
{"Municipality":"NMBM","SGC":"12345"}
The error message you're receiving is coming from the stageapi.myprepaid.co.za server. This is the full response when I point it back to them:
HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 30 Aug 2013 04:30:41 GMT
Connection: close
Content-Length: 867
{"Message":"An error has occurred.","ExceptionMessage":"Object reference not set to an instance of an object.","ExceptionType":"System.NullReferenceException","StackTrace":" at MyPrepaidApi.Controllers.ConsumerRegisterRequestController.Put(CrmRegisterRequest value) in c:\\Workspace\\MyPrepaid\\Prepaid Vending System\\PrepaidCloud\\WebApi\\Controllers\\ConsumerRegisterRequestController.cs:line 190\r\n at lambda_method(Closure , Object , Object[] )\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)\r\n at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)"}
You may want to check out the API to make sure you're passing them the correct information. If you are, the problem could be on their end.
And while I realize this isn't part of your question and this is in development, please remember to sanitize any data from $_GET. :)
Try with:
curl_setopt($ch, CURLOPT_PUT, true);

Categories