Perl: Programatically set POST param using REST::Client module - perl

I've built a REST Server and now I want to rapidly test it from a Perl Client, using REST::Client module.
It works fine if I perform GET Request (explicitly setting parameters in the URL) but I can't figure out how to set those params in POST Requests.
This is how my code looks like:
#!/usr/bin/perl
use strict;
use warnings;
use REST::Client;
my $client = REST::Client->new();
my $request_url = 'http://myHost:6633/my_operation';
$client->POST($request_url);
print $client->responseContent();
I've tried with something similar to:
$client->addHeader ('my_param' , 'my value');
But it's clearly wrong since I don't want to set an HTTP predefined Header but a request parameter.
Thank you!

It quite straight forward. However, you need to know what kind of content the server expects. That will typically either be XML or JSON.
F.ex. this works with a server that can understand the JSON in the second parameter, if you tell it what it is in the header in the third parameter.
$client->POST('http://localhost:3000/user/0/', '{ "name": "phluks" }', { "Content-type" => 'application/json'});

The REST module accepts a body content parameter, but I found to make it work with a string of parameters, you need to set a proper content type.
So the following code works for me:
$params = $client->buildQuery([username => $args{username},
password => $args{password}]);
$ret = $client->POST('api/rest/0.001/login', substr($params, 1),
{'Content-type' => 'application/x-www-form-urlencoded'});

I've not used the REST module, but looking at the POST function, it accepts a body content parameter, try creating a string of the parameters and send that within the function
$client->POST($request_url, "my_param=my+value");
print $client->responseContent();

Related

Getting "A service address has not been specified..." when using Perl SOAP::Lite to do call using wsdl

I'm trying to do a SOAP call to http://www.webservicex.net/ConvertSpeed.asmx using the WSDL.
My code is as follows
use SOAP::Lite;
my $client = SOAP::Lite->new;
$client = $client->service( "http://www.webservicex.net/ConvertSpeed.asmx?wsdl" );
my $soapResp = $client->call( "ConvertSpeed", 100, "kilometersPerhour", "milesPerhour" );
if ( $soapResp->fault ) {
print $soapResp->faultstring;
}
else {
print $soapResp->result;
}
This gives me the following error
A service address has not been specified either by using SOAP::Lite->proxy() or a service description)
I believe that the WSDL is supposed to provide the service address, but it does not seem to be doing that here.
I have tried setting the service address to http://www.webservicex.net/ConvertSpeed.asmx via $client->proxy("http://www.webservicex.net/ConvertSpeed.asmx") after $client->service(), but that just gives me an error saying:
Server did not recognize the value of HTTP header SOAPAction: #ConvertSpeed
I assumed the WSDL was supposed to provide the service address and so omitted the $client->proxy() bit and tried other stuff.
This ranged from chaining methods differently and eventually chaining as little as possible (the code above) to changing the way the call is made.
From this answer I tried
$client->ConvertSpeed(100, "kilometersPerhour", "milesPerhour")
which just seems to return nothing.
This is running on Perl 5.10 and version 0.714 of SOAP::Lite.
I'm not sure what else to try, but I have confirmed that the same SOAP call works from Python(see edit).
EDIT:
from zeep import Client
client = Client('http://www.webservicex.net/ConvertSpeed.asmx?wsdl')
result = client.service.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour')
print(result)
I posted this question to PerlMonks as well since it didn't seem to be doing well here. According to the answer I received there SOAP::Lite isn't ideal for a complex service using the WSDL. Instead everything should be specified using the uri() and proxy() methods. If the WSDL is to be used then XML::Compile::WSDL11 is the module to look at.
The answer supplies working code for the call using SOAP::Lite without the WSDL as follows :
$client = SOAP::Lite->new(
uri => 'http://www.webserviceX.NET/',
proxy => 'http://www.webservicex.net/ConvertSpeed.asmx',
);
$client->on_action(sub {"http://www.webserviceX.NET/ConvertSpeed"});
$client->autotype(0);
$sheader = SOAP::Header->new('');
$sheader->type(xml => '');
$client->ns('http://www.webserviceX.NET/','web');
$client->envprefix('soapenv');
push #request, SOAP::Data->name(speed => 100)->prefix('web');
push #request, SOAP::Data->name(FromUnit => 'kilometersPerhour')->prefix('web');
push #request, SOAP::Data->name(ToUnit => 'milesPerhour')->prefix('web');
$soapResp = $client->ConvertSpeed($sheader,#request);
print $soapResp->result,"\n";
If I get a working example using XML::Compile::WSDL11 I will post it here. As I am unsure if I am going to look into it soon I decided to at least post this since the question is more focused on using a WSDL with SOAP::Lite

How to receive json in Dancer?

I am very new to Perl framework Dancer. As of now I have a get http listener working. I have an Angular framework trying to post a json string to Dancer. How can I retreive the json and perhaps assign it to a scalar variable ($json).
get '/games' => sub {
header 'Access-Control-Allow-Origin' => '*';
&loadgames();
return $games;
};
post '/newgame' => sub {
header 'Access-Control-Allow-Origin' => '*';
#what should i put here to retrieve the json string
#I plan to pass the json string to a sub to convert to XML
};
I am not sure If I chose Dancer as backend framework that will get and post data.
Thanks for the help!
If your HTTP request has a JSON body (Content-type: application/json) rather than being an HTML form post, then you probably want something like this:
post '/url-path' => {
my $post = from_json( request->body );
# do something with the POSTed data structure
# which would typically be a hashref (or an arrayref)
# e.g.: schema->resultset('Widget')->create($post);
}
The from_json routine is one of the DSL Keywords provided by Dancer.
Dancer provides the params keyword for accessing route, body, and query parameters. You want a body parameter. Exactly which body parameter you want will depend on the name of the field you posted it to the route with (look at your form or your ajax request).
my $json_string = params('body')->{$field_name}
You can also use param, if you don't have any conflicting parameter names in the route or query parameters.
Once you have the json, remember it's just a string at the moment. You might want to read it into a perl data structure: Dancer provides from_json for this purpose.
As an aside: I notice in your get route, you call a function loadgames in void context, and then return a variable you haven't declared (or perhaps you have set it as a global - but do you need it to be a global?). I recommend beginning each perl file with use strict; to pick up issues like this. I suspect you probably just want to use the return value of loadgames as your return value.

Route passing in Sinatra for single page app with REST resources

I'm trying to make a single page website with sinatra in the backend. I want all GET-requests with preferred accept-header "text/html" to return the same page, BUT all requests that wants json to get json-data.
Example:
A GET call to '/users' with accept set to 'text/html' should return index.html.
A GET call to '/users' with accept set to 'application/json' should return the JSON-data with users.
I have tried using a catch-all method for html and using accept-checks like this:
# Generic html giver for angular routing
get //, :provides => 'html' do
pass unless request.preferred_type.to_str == 'text/html'
erb :index
end
# Give users as JSON
get '/users', :provides => 'json' do
pass unless request.preferred_type.to_str == 'application/json'
'["dummy", "array"]'
end
...but it doesn't seem to pass to the second route when preferred_type isn't text/html.
Note: I'm useing the string check on preferred_type, since using request.accept? catches all with browsers giving "*/*" as last accept header.
Oh, it seems like the culprit is:
:provides => 'json'
Without it, the passing works as expected. I guess it's a bug then.

Perl WebService::Soundcloud - how to pass track parameters while uploading to Soundcloud

I'm trying to upload a sound to soundcloud using WebService::Soundcloud. I've so far been able to make a couple of GET/POST requests following the examples provided by the WebService::Soundcloud documentation.
However, I can't find a decent example anywhere on how to do an upload while passing the required parameters i.e. track, and within it, asset_data, title e.t.c. I'm wondering if I should be sending out a multipart message.
Any examples will be appreciated!
Also, here's what I have tried so far: After authenticating and getting a valid WebService::Soundcloud instance.
GET my $response = $scloud->get( '/me/tracks' );
PUT my $response = $scloud->put( '/me/tracks/91576621', JSON::to_json({track=>{title=>"My test title",description=>"My test description"}}) );
POST my $file = '/home/ski/track1.mp3';
my $asset_data = File::Slurp::read_file( $file, binmode => ':raw' );
my $response = $scloud->post('/me/tracks', '{"track":{"title":"My test title","asset_data":"'.$asset_data.'"}}' );
This fails with "Request entity contains invalid byte sequence. Please transmit valid UTF-8"
The example you provide manually constructs the JSON but doesn't take into account the binary nature of the file content which means it's unlikely to work. It's also vulnerable to abusive content changing your JSON due to lack of escaping/proper encoding.
The documentation you cite has a put example which demonstrate the content needs to be encoded into JSON and then passed to the library. I've not used this api but it's probably a simple case of using encode_json as per the examples. I'll just show an example that's equivalent to your manual encoding:
use JSON qw(encode_json);
my $asset_data = "ascii, quotes (\"'), non-ascii: \000\001\002\003";
my $content = encode_json({ track => { title => "My test title",
asset_data => $asset_data}});
print $content . "\n"; ### inspection of encoding
And this shows that JSON uses a UTF-8 representation to deal with binary data:
{"track":{"asset_data":"ascii, quotes (\"'), non-ascii: \u0000\u0001\u0002\u0003","title":"My test title"}}
The key/values are being re-ordered there but it's equivalent JSON.

How can I access parameters passed in the URL when a form is POSTed to my script?

I've run into an issue with mod_rewrite when submitting forms to our site perl scripts. If someone does a GET request on a page with a url such as http://www.example.com/us/florida/page-title, I rewrite that using the following rewrite rule which works correctly:
RewriteRule ^us/(.*)/(.*)$ /cgi-bin/script.pl?action=Display&state=$1&page=$2 [NC,L,QSA]
Now, if that page had a form on it I'd like to do a form post to the same url and have Mod Rewrite use the same rewrite rule to call the same script and invoke the same action. However, what's happening is that the rewrite rule is being triggered, the correct script is being called and all form POST variables are being posted, however, the rewritten parameters (action, state & page in this example) aren't being passed to the Perl script. I'm accessing these variables using the same Perl code for both the GET and POST requests:
use CGI;
$query = new CGI;
$action = $query->param('action');
$state = $query->param('state');
$page = $query->param('page');
I included the QSA flag since I figured that might resolve the issue but it didn't. If I do a POST directly to the script URL then everything works correctly. I'd appreciate any help in figuring out why this isn't currently working. Thanks in advance!
If you're doing a POST query, you need to use $query->url_param('action') etc. to get parameters from the query string. You don't need or benefit from the QSA modifier.
Change your script to:
use CGI;
use Data::Dumper;
my $query = CGI->new; # even though I'd rather call the object $cgi
print $query->header('text/plain'), Dumper($query);
and take a look at what is being passed to your script and update your question with that information.