How to receive json in Dancer? - perl

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.

Related

How to change Mojolicious Lite default error not found to a custom json response

I'm creating a json web service using Mojolicious Lite.
By default Mojolicious returns a HTML response for a server error or not found error.
Is there a way to overwrite this to a custom JSON response?
Here are two approaches:
Use json as the app's default format and use a not_found.*.json.ep template
use Mojolicious::Lite;
app->renderer->default_format('json');
app->start;
__DATA__
## not_found.development.json.ep
{"not":"found","code":404,"data":{"key1":"value1","key2":[42,19,"value3"]}}
Override json payload with a before_render hook.
use Mojolicious::Lite;
hook before_render => sub {
my ($c,$args) = #_;
if ($args->{template} && $args->{template} eq 'not_found') {
$args->{json} = { "too bad" => "so sad" };
}
};
app->start;
It's been a minute, but in Mojo 9 in a full app I've just been returning JSON and returning the status:
$c->render( json => $json, status => 404 );
But, I also have a catch-all route at the end of my setup:
$self->routes->any('/*')->to( ... );
Note, however, that there are some decisions to make about HTTP codes and application-level messaging. For example, accessing a defined and valid endpoint that returns zero search results could easily return 200 and an empty JSON array. The endpoint was there, the server knew how to handle it, and zero list items can be seen as valid as any other number. See I've been abusing HTTP Status Codes in my APIs for years, for example.
The Rendering guide discusses how to customize these responses.

Turn Query String into Hash Perl

I want to find out how to do the opposite of this:
Perl - How can I turn a hash into a query string?
I have a query string form that is stringified and want to convert it to a hash.
With more context, a client is passing some URL params as a stringified query form which is base64 encoded, and I wanted to decode, turn into a hash, add some more custom URL parameters to the hash, then I have a method that already converts back to a string and appends to the URL.
my $uriQuery = URI::Query->new($redirectQueryParams);
$qs = $uriQuery->hash;
Seems to blow up with
Can't locate object method "new" via package "URI::Query"
The URI module in conjunction with the drop-in addon URI::QueryParams can do what you want.
use strict;
use warnings;
use URI;
use URI::QueryParam;
my $uri = URI->new('https://example.org/?foo=bar&baz=qrr');
$uri->query_param_append(frobnication => 'yes, please');
print $uri;
This will print
https://example.org/?foo=bar&baz=qrr&frobnication=yes%2C+please
If you actually wanted a hash, you could call the query_form_hash method.
$uri->query_form_hash
However, that doesn't make much sense considering that all the alterations you could think of are already implemented.

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.

Given a query string and the empty form html it came from generate filled in html

I have an html with empty forms and I have the query string that was generated when filling in those forms.
How can I merge them together into a filled in html page?
Hopefully you can give me a perl based solution.
Edit: I have a web scraper based on WWW::Mechanize with perl. I am saving the html content to generate a hmtl slideshow of the session, however I can't save the html with the filled-in values.
I have looked at mechanize source and the it is creating a HTML::Form object to handle the forms. I have looked at HTML::Form and I don't see how I can turn the object back to html, there is just a dump method.
There is a section in HTML::Form code that lets me generate the POST or GET request and I thought that maybe that was a good starting point to generate filled-in hmtl by merging the request with the original html.
if ($method eq "GET") {
require HTTP::Request;
$uri = URI->new($uri, "http");
$uri->query_form(#form);
return HTTP::Request->new(GET => $uri);
}
elsif ($method eq "POST") {
require HTTP::Request::Common;
return HTTP::Request::Common::POST($uri, \#form,
Content_Type => $enctype);
}
So I can use that code snipet in my mechanize program after I am done filling forms to get the final POST or GET request but that is as far as I go :(
I am assuming you want to show the form with its values.
All you have to do is set the value of a form element to be the value from the query string.
if you give more info, we can tell you more specific answers...

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

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();