This question follows on from my previous one. I thought I would perform a basic token authentication by calling the get_space_usage API function. I tried
$headers = array("Authorization: Bearer token",
"Content-Type:application/json");
$ch = curl_init('https://api.dropboxapi.com/2/users/get_space_usage/');
curl_setopt($ch,CURLOPT_HTTPHEADER,$headers);
curl_setopt($ch,CURLOPT_POST,true);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
The documentation does not in fact indicate that it is necessary to provide a Content-Type header. However, without that header I get the message
Bad HTTP "Content-Type" header: "application/x-www-form-urlencoded". Expecting one of "application/json",...
Putting in that header but supplying no POST fields produces another error
request body: could not decode input as JSON
Just providing some dummy post data curl_setopt($ch,CURL_POSTFIELDS,json_encode(array('a'=>1))); does not do anything to remedy the situation. What am I doing wrong?
The documentation doesn't indicate that a Content-Type header is expected, because, since this endpoint doesn't take any parameters, no body is expected, and so there's no content to describe via a Content-Type header. Here's a working command line curl example, per the documentation:
curl -X POST https://api.dropboxapi.com/2/users/get_space_usage \
--header "Authorization: Bearer <ACCESS_TOKEN>"
Translating this to curl in PHP would involve making sure PHP also doesn't send up a Content-Type header. By default though, it apparently sends "application/x-www-form-urlencoded", but that isn't accepted by the API. If you do set a "application/json", the API will attempt to interpret the body as such, but won't be able to do so, since it isn't valid JSON, and so fails accordingly.
It's apparently not easy (or maybe not possible) to omit the Content-Type header with curl in PHP, so the alternative is to set "application/json", but supply valid JSON, such as "null". Here's a modified version of your code that does so:
<?php
$headers = array("Authorization: Bearer <ACCESS_TOKEN>",
"Content-Type: application/json");
$ch = curl_init('https://api.dropboxapi.com/2/users/get_space_usage');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, "null");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
Related
Does RESTful APIs provides url encoding as default or do I have to encode it using other methods? I am using following code:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POSTFIELDS,
"xmlRequest=" . $input_xml);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300);
$data = curl_exec($ch);
curl_close($ch);
There is no 'standard REST'. So it's often very difficult to say something specific, because in some ways it's just an idea.
But if you restrict REST to HTTP apis, generally the 'best practice' is that you use url encoding, where this is expected/needed by HTTP and don't use url encoding where it's unexpected.
To get a better answer, rephrase the question with a specific example that made you wonder about this.
It is either not being sent, or not being received correctly. Using curl direct from the command line (using the -d option) or from PHP (using CURLOPT_POSTFIELDS) does work.
I start with a PSR-7 request:
$request = GuzzleHttp\Psr7\Request('POST', $url);
I add authentication header, which authenticates against the API correctly:
$request = $request->withHeader('Authorization', 'Bearer ' . $accessToken);
Then I add the request body:
// The parameter for the API function
$body = \GuzzleHttp\Psr7\stream_for('args=dot');
$request = $request->withBody($body);
I can send the message to the API:
$client = new \GuzzleHttp\Client();
$response = $client->send($request, ['timeout' => 2]);
The response I get back indicates that the "args" parameter was simply not seen by the API. I have tried moving the authentication token to the args:
'args=dot&access_token=123456789'
This should work, and does work with curl from the command line (-d access_token=123456789) but the API fails to see that parameter also when sending cia curl (6.x) as above.
I can see the message does contain the body:
var_dump((string)$request->getBody());
// string(8) "args=dot"
// The "=" is NOT URL-encoded in any way.
So what could be going wrong here? Are the parameters not being sent, or are they being sent in the wrong format (maybe '=' is being encoded?), or is perhaps the wrong content-type being used? It is difficult to see what is being sent "on the wire" when using Guzzle, since the HTTP message is formatted and sent many layer deep.
Edit: Calling up a local test script instead of the remote API, I get this raw message detail:
POST
CONNECTION: close
CONTENT-LENGTH: 62
HOST: acadweb.co.uk
USER-AGENT: GuzzleHttp/6.1.1 curl/7.19.7 PHP/5.5.9
args=dot&access_token=5e09d638965288937dfa0ca36366c9f8a44d4f3e
So it looks like the body is being sent, so I guess something else is missing to tell the remote API how to interpret that body.
Edit: the command-line curl that does work, sent to the same test script, gives me two additional header fields in the request:
CONTENT-TYPE: application/x-www-form-urlencoded
ACCEPT: */*
I'm going to guess it is the content-type header which is missing from the Guzzle request which is the source of the problem. So is this a Guzzle bug? Should it not always sent a Content-Type, based on the assumptions it makes that are listed in the documentation?
The Content-Type header was the issue. Normally, Guzzle will hold your hand and insert headers it deems necessary, and makes a good guess at the Content-Type based on what you have given it, and how you have given it.
With Guzzle's PSR-7 messages, none of that hand-holding is done. It strictly leaves all the headers for you to handle. So when adding POST parameters to a PSR-7 Request, you must explicitly set the Content-Type:
$params = ['Foo' => 'Bar'];
$body = \GuzzleHttp\Psr7\stream_for(http_build_query($params));
$request = $request->withBody($body);
$request = $request->withHeader('Content-Type', 'application/x-www-form-urlencoded');
The ability to pass in the params as an array and to leave Guzzle to work out the rest, does not apply to Guzzle's PSR-7 implementation. It's a bit clumsy, as you need to serialise the POST parameters into a HTTP query string, and then stick that into a stream, but there you have it. There may be an easier way to handle this (e.g. a wrapper class I'm not aware of), and I'll wait and see if any come up before accepting this answer.
Be aware also that if constructing a multipart/form-data Request message, you need to add the boundary string to the Content-Type:
$request = $request->withHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
Where $boundary can be something like uniq() and is used in construction the multipart body.
The GuzzleHttp\Client provides all necessary wrapping.
$response = $client->post(
$uri,
[
'auth' => [null, 'Bearer ' . $token],
'form_params' => $parameters,
]);
Documentation available Guzzle Request Options
Edit: However, if your requests are being used within GuzzleHttp\Pool then, you can simply everything into the following:
$request = new GuzzleHttp\Psr7\Request(
'POST',
$uri,
[
'Authorization' => 'Bearer ' . $token,
'Content-Type' => 'application/x-www-form-urlencoded'
],
http_build_query($form_params, null, '&')
);
I am learning to write my first IOS app that will query some basic OIDs from a Proliphix IP Network Thermostat. Proliphix supports several methods such as Curl; PHP and API GET & SET. Out of these methods, what would be the easiest in Swift?
Can someone tell me how to convert one of the the following methods for Swift?
Here are examples of these from the Proliphix API that can be found on a google search.
Curl
Get
curl –u hostname:password –-data OID1.1= http://192.168.1.100:8100/get
Set
curl –u hostname:password --data OID1.10.5=120 --data submit=Submit
http://192.168.1.100:8100/pdp
API GET
The URL used is /get. An API GET request is a list of OIDs where their value is not specified. A properly formatted request should provide the Content-Length header. . The entry is the encoded basic authentication word (See RFC 2617 -HTTP Authentication: Basic and Digest Access Authentication).
Request
POST /get HTTP/1.1
Authorization: Basic <credentials>
Content-Type: application/x-www-form-urlencoded
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: 192.168.111.114:8214
Content-Length: 92
OID1.10.9=&OID1.2=&OID1.1=&OID1.4=&OID1.8=&OID2.7.1=&
Response
HTTP/1.1 200 OK
Cache-control: no-cache
Server: Ubicom/1.1
Content-Length: 166
OID1.10.9=example#proliphix.com&OID1.2=SW Dev 114&OID1.1=therm_rev_2 0.1.40&OID1.4=192.168.111.114&OID1.8=00:11:49:00:00:58&OID2.7.1=NT100
API SET
The URL used is /pdp . An API SET is similar to the API GET for the request message, except that the desired value is provided at the equals sign. The response is formatted differently. The entry is the encoded basic authentication word (See RFC 2617 -HTTP Authentication: Basic and Digest Access Authentication). The last item in the request must be “submit=Submit”. Do not include an ‘&’ after the "submit=Submit".
Request
POST /pdp HTTP/1.1
Authorization: Basic <credentials>
Content-Type: application/x-www-form-urlencoded
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: 192.168.111.114:8214
Content-Length: 193
Response
HTTP/1.1 200 OK
Cache-control: no-cache
Server: Ubicom/1.1
Content-Length: 308
PHP
PHP is a web-server specific scripting language, akin to mod_perl. It integrates well into Apache and offer many web-specific libraries as part of the base system.
Get
$oids = array('OID1.4'=>'', // commonIpAddr
'OID1.10.5'=>'',
‘submit’=>’Submit’); // commonCallhomeInterval
$url = “http://192.168.1.100:8100/get”;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPGET, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$myHeader = array("Content-Type: application/x-www-form-urlencoded" ); curl_setopt($ch, CURLOPT_HTTPHEADER, $myHeader);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($oids)); $response = curl_exec($ch);
curl_close($ch);
$oids = array();
parse_str($response, $oids); // converts '.' to underscore
$localip = $oids['OID1_4'];
$interval = $oids['OID1_10_5']; // in minutes
I would say that you should use the API that Proliphix is providing.
As you can see, they provide an example, and you've already managed to figure out how to provide the correct parameters through cURL so now you "just" need to convert this to Swift.
For this you need a HTTP networking API, you could use either the NSURLSession API provided by Apple, or perhaps Alamofire, just to mention a pair.
These API's take an URL which would be /get or /pdp in your case. Then you need to tell them wether this is a GET or a POST request. If the API needs any data (like the OID parameters in your case), you'll need to provide that as well and then you need to set up eventual headers.
Then you send your request and wait for an answer, which you then react to.
Here is an example on how to do this with NSURLSession:
if let url = NSURL(string: "http://httpbin.org/post"){
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST" //Or GET if that's what you need
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") //This is where you add your HTTP headers like Content-Type, Accept and so on
let params = ["OID1.2" : "SW+Dev+114", "OID1.4" : "192.168.111.114"] as Dictionary<String, String> //this is where you add your parameters
let httpData = NSKeyedArchiver.archivedDataWithRootObject(params) //you need to convert you parameters to NSData or to JSON data if the service accepts this, you might want to search for a solution on how to do this...hopefully this will get you in the right direction :-)
request.HTTPBody = httpData
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request, completionHandler: { (returnData, response, error) -> Void in
var strData = NSString(data: returnData, encoding: NSUTF8StringEncoding)
println("\(strData)")
}).resume() //Remember this one or nothing will happen :-)
}
Hope this gets you in the right direction. You could also do a Google search for NSURLSession or Alamofire tutorial, now that you know what to search for.
I am totally new to tcl.
I am trying to send a post request to a server. Those are the headers I am trying to send :
"POST test HTTP/1.1"
"Content-Type:application/x-www-form-urlencoded"
"keep-alive:true"
"Authorization: Basic khjkjkhkhkfzxc"
"client: amin"
"Content-Length:10"
"ggggsis"
My code is :
set bs "Basic khjkjkhkhkfzxc"
set hd [list Authorization $bs]
set tk[http::geturl $url -headers $hd]
set res [http::data $tk]
http::cleanup $tk
return $tk
Why doesn't this work? Where is my mistake? And how to fix it?
Your help is much appreciated. Thanks
EDIT: I created a small server socket to display my request and here is my new code for post:
set bs "Basic gfhfghfdhgf"
set hd [list Authorization $bs]
set token [http::geturl $url -headers $hd -query [::http::formatQuery blabla blablo] -type "text/xml"]
puts "done"
set postStat [http::status $token]
set postResp [http::data $token]
puts $postResp
puts "ok"
puts $postStat
Here is what it is being displayed
Accepted connection from 127.0.0.1 at Fri Aug 1 10:51:22 EDT 2014
POST /QUEUE/test HTTP/1.0
Accept: */*
Host: localhost:9999
User-Agent: Tcl http client package 2.5.0
Authorization: Basic ZGVmYXVsdCJkZWZhdWx0
Content-Type: text/xml
Content-Length: 13
The problem is that the program send the post but is doesn't move out from
set token [http::geturl $url -headers $hd -query [::http::formatQuery blabla blablo] -type "text/xml"],
And the httpget query is never being sent ("done" is never being printed and the content of the query doesn't print in the server side)
Sorry for if I explain things badly.
Thanks
It's rather simpler to build the headers you want to send as a dictionary:
dict set hdr Authorization "Basic [binary encode base64 ${user}:${pass}]"
dict set hdr Client amin
The other headers you talk about are either correct by default (the content type) or are best left to the http library to handle.
Once you've got the headers and the data, you send the POST like this:
set token [http::geturl $theurl -headers $hdr -query $body]
set responseBody [http::data $token]
# Access headers here...
http::cleanup $token
# The token is no longer valid here
Note that you can only access the results of the query until the http::cleanup command is called on it. After that, the results are gone (unless you stored them in a variable elsewhere, of course).
You'd better make sure that the $body is formatted right. The http::formatQuery command is designed for this:
set body [http::formatQuery fredKey fredValue daveKey daveValue foo bar]
I am trying to get an access token for Paypal's RESTful web services but unfortunately not making any headway. This is my first time dealing with REST, so please be patient with me :)
Here is what I have:
Client_id and secret as provided by Paypal for a sandbox account through the paypal developer website.
The ENDpoint: https://api.sandbox.paypal.com/v1/oauth2/token
The documentation that i am referring to is : https://developer.paypal.com/webapps/developer/docs/integration/direct/make-your-first-call/
Now the juicy part of making that API call. I am developing in PHP so I am using CURL to make the calls. something like this;
const CLIENT_ID = ****..*** ;
const SECRET = ***..***;
$base64EncodedClientID = base64_encode(self::CLIENT_ID . ":" . self::SECRET);
$headers = array("Authorization" => "Basic " . $base64EncodedClientId, "Accept" =>"*/*", "Content-type" => "multipart/form-data");
$params = array("grant_type"=>"client_credentials");
$url = "https://api.sandbox.paypal.com/v1/oauth2/token";
$ch = curl_init();
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_HEADER, $headers);
curl_setopt($ch,CURLOPT_POSTFIELDS,$params);
$response = curl_exec($ch);
Pretty vanilla right? Except that I do not get the JSON response that I expect from Paypal but false. This implies that my CURL request was not prepared well, perhaps I am setting the header incorrectly or the params are incorrect. Regardless, the URL is definitely accessible since I was able to access it through command line with the same credentials and got the desired JSON response.
The one glaring problem I have with the above code is that I am providing the client_id and secret as a header option. basic sense tells me that they need to be part of the POST field data However, if you look at line 89 of this Github code https://github.com/paypal/rest-api-sdk-php/blob/master/lib/PayPal/Auth/OAuthTokenCredential.php (Paypals' official PHP REST SDK), it clearly states that the credentials are being set in the header field.
Where am I messing up ?
With curl you don't need to manually generate the base64 encoded value for the Authorization header just use the CURLOPT_USERPWD option and pass the clientID and secret as the user:pwd.
curl_setopt($curl, CURLOPT_USERPWD, $clientId . ":" . $clientSecret);
here is a sample - look for the get_access_token() method:
https://github.com/paypal/rest-api-curlsamples/blob/master/execute_all_calls.php
Had the exact same problem you ran into. The issue is that PayPal accepts the content-type application/x-www-form-urlencoded. Your code is attempting to send multipart/form-data. CURL by default sends application/x-www-form-urlencoded, but you are passing your data as an array. Instead, you should be passing the data like a url encoded string since this is what application/x-www-form-urlencoded data looks like:
$params = "grant_type=client_credentials";
Your headers have the same problem. Pass it as an array of strings instead of a dictionary. For instance:
$headers = ["Authorization Basic " . $base64EncodedClientId];
Also, you don't need those other two headers you passed in. The 'Accept' header does nothing since you are accepting everything, and the Content-type is wrong for one, and two is defaulted to 'application/x-www-form-urlencoded' by CURL so unless you need to override that, there is no need.