Home: Perl Programming Help: Beginner:
How to write simple API for my cgi script?



Master_Sergius
Novice

Aug 12, 2018, 2:02 AM


Views: 1672
How to write simple API for my cgi script?

Hello, Perl community! I'm new in Perl world and as far as I can see - there are a lot of ways to code something in Perl.
I have cgi script, which looks up for word forms in database with simple UI (yes, it's not pretty):


Code
#!/usr/bin/perl -w 

use strict;
use warnings;
use CGI qw(:standard);
use utf8;
use Encode;

use Database;

my $cgi = CGI->new();
print $cgi->header(-charset => 'utf-8'),
$cgi->start_html(-title => 'Word finder app', -encoding => 'utf8');

my $ENV;

my $uri = $cgi->path_info;
print "uri: $uri";

$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;
if ($ENV{'REQUEST_METHOD'} eq "POST") {
my $word = param('word_input');
my $res;
if (param('word_part')) {
$res = get_words($word);
} elsif (param('word_form')) {
$res = get_word_form($word);
};
draw_page($res);
} else {
draw_page();
}

sub draw_page {
my $res = shift;
print '<div style="float:left; width:100%">';
my $input_form = div({-align => 'center'},
h3('Enter word or part of word'),
start_form(-action => '/cgi-bin/words_cgi.pl',
-method => 'post',
-name => 'form_word_input',
-id => 'form_word_input'),
textfield(-name => 'word_input',
-id => 'word_input'),
br,
submit(-name => 'word_part',
-form => 'form_word_input',
-value => 'find all words'),
submit(-name => 'word_form',
-form => 'form_word_input',
-value => 'find main form'),
end_form);
print div({-style => 'float: left; width: 70%'}, $input_form);

my $dbh = new Database();
my $history_records = '';
foreach my $key (keys $dbh->get_cache()) {
$history_records .= "$key<br/>";
}

my $history_div = div({-align => 'center'},
h5('History of queries'),
$history_records);
print div({-style => 'float: right; width: 30%'}, $history_div);

print '</div>';
print '<div style="float:left;">';
if ($res) {
print '<table cellspacing="2" border="1" cellpadding="5">';
foreach my $word_res (@{$res}) {
print "<tr><td>$word_res</td></tr>";
};
print '</table>';
}
print '</div>';
};

sub get_words {
my $word_part = shift;
my $limit = 10;
my $dbh = new Database();
$dbh->connect('perl_crud');
my $res = $dbh->select_data('SELECT uw_form FROM ua_wf
WHERE uw_form LIKE ? LIMIT ?',
['%'.$word_part.'%', $limit]);

$dbh->disconnect();
return $res;
};

sub get_word_form {
my $word = shift;
my $dbh = new Database();
$dbh->connect('perl_crud');
my $res = $dbh->select_data('SELECT ut_root FROM ua_root WHERE ut_id IN
(SELECT uw_ut_id FROM ua_wf WHERE uw_form = ?)',
[$word]);
$dbh->disconnect();
return $res;
};


exit(0);


Now I want to write simple API to use this script in console or other script. For example if I type: <host>/v1/get_words?word=<some_word>, I should get response in JSON format.
What I should use here? How to add this to my script properly?
I can use $cgi->path_info, or CGI::Application::Dispatch, or some REST framework or something else??? And how?


Update #1
Well, I did this variant, it works. But, I'm sure, it's not the best:


Code
#!/usr/bin/perl -w 

use strict;

use CGI qw(:standard);
use Encode;
use JSON;
use utf8;

use Database;

my $cgi = CGI->new();

sub draw_page {
my $res = shift;
print '<div style="float:left; width:100%">';
my $input_form = div({-align => 'center'},
h3('Enter word or part of word'),
start_form(-action => '/cgi-bin/words_cgi.pl',
-method => 'post',
-name => 'form_word_input',
-id => 'form_word_input'),
textfield(-name => 'word_input',
-id => 'word_input'),
br,
submit(-name => 'word_part',
-form => 'form_word_input',
-value => 'find all words'),
submit(-name => 'word_form',
-form => 'form_word_input',
-value => 'find main form'),
end_form);
print div({-style => 'float: left; width: 70%'}, $input_form);

my $dbh = new Database();
my $history_records = '';
foreach my $key (keys $dbh->get_cache()) {
$history_records .= "$key<br/>";
}

my $history_div = div({-align => 'center'},
h5('History of queries'),
$history_records);
print div({-style => 'float: right; width: 30%'}, $history_div);

print '</div>';
print '<div style="float:left;">';
if ($res) {
print '<table cellspacing="2" border="1" cellpadding="5">';
foreach my $word_res (@{$res}) {
print "<tr><td>$word_res</td></tr>";
};
print '</table>';
}
print '</div>';
};


sub get_words {
my $word_part = shift;
my $limit = 10;
my $dbh = new Database();
$dbh->connect('perl_crud');
my $res = $dbh->select_data('SELECT uw_form FROM ua_wf
WHERE uw_form LIKE ? LIMIT ?',
['%'.$word_part.'%', $limit]);

$dbh->disconnect();
return $res;
};


sub get_word_form {
my $word = shift;
my $dbh = new Database();
$dbh->connect('perl_crud');
my $res = $dbh->select_data('SELECT ut_root FROM ua_root WHERE ut_id IN
(SELECT uw_ut_id FROM ua_wf WHERE uw_form = ?)',
[$word]);
$dbh->disconnect();
return $res;
};


sub check_api_call {
my %api_endpoints = (
'/v1/words/get_all_words' => sub { my $word = shift; return {'data' => get_words($word)} },
'/v1/words/get_root_form' => sub { my $word = shift; return {'data' => get_word_form($word)} },
'error' => sub { return {'error' => 'uknown api endpoint'} }
);
my $uri = $cgi->path_info;
if ($uri) {
my $word = param('word_input');
my $api_method = $api_endpoints{$uri} || $api_endpoints{'error'};
my $res = $api_method->($word);
return encode_json($res);
}
}

sub main {

my $res = check_api_call();
if ($res) {
print $cgi->header('application/json');
print $res;
} else {
print $cgi->header(-charset => 'utf-8'),
$cgi->start_html(-title => 'Word finder app', -encoding => 'utf8');
my $ENV;
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;
if ($ENV{'REQUEST_METHOD'} eq "POST") {
my $word = param('word_input');
my $res;
if (param('word_part')) {
$res = get_words($word);
} elsif (param('word_form')) {
$res = get_word_form($word);
};
draw_page($res);
} else {
draw_page();
}
};
};


main();

exit(0);



(This post was edited by Master_Sergius on Aug 12, 2018, 8:41 AM)


BillKSmith
Veteran

Aug 12, 2018, 3:38 PM


Views: 1659
Re: [Master_Sergius] How to write simple API for my cgi script?

You are right, there are a lot of ways to code in Perl. In fact, most Perl programmers are proud of what they call "TIMTOWTDI" ("There is more than one way to do it").

I know that the CGI module is considered obsolete, and discouraged in new code. I am not familiar with newer replacements.

You have one minor error that suggests that you may have a serious lack of understanding of Perl data types. You declare the scalar $ENV as a lexical variable and never use it. The hash %ENV (which you do use) is a special (built-in) variable. You can access perl's own documentation for it by typing at the command line:

Code
perldoc perlvar


Other than that, your code looks good.
Good Luck,
Bill


Master_Sergius
Novice

Aug 13, 2018, 11:34 AM


Views: 1651
Re: [BillKSmith] How to write simple API for my cgi script?

Thank you. Your answer is really helpful.


Zhris
Enthusiast

Aug 19, 2018, 8:31 AM


Views: 1605
Re: [Master_Sergius] How to write simple API for my cgi script?

Hi,


Quote
Now I want to write simple API to use this script in console or other script. For example if I type: <host>/v1/get_words?word=<some_word>, I should get response in JSON format.
What I should use here?


With your first script, most simply, you could handle both GET and POST request methods, and add a new query parameter "json", which if true serializes the response data structure as json instead of html. But from your second script it looks as though you have handled this in your own way.


Quote
How to add this to my script properly? I can use $cgi->path_info, or CGI::Application::Dispatch, or some REST framework or something else??? And how?


As you are aware, CGI is outdated and basically deprecated. I recommend using a modern web application framework, one of what I call, "the big three", Dancer2, Mojolicious or Catalyst. There are others available such as CGI::Application::Dispatch that you mentioned, but those I listed are featuresome and there is much more support.

I recommend starting with Dancer2, its simpler to learn than the others. Its focus is specifically on interfacing the typically low level side of things, but there are various core engines that handle common components such as sessions and templates, and plugins that extend its functionality such as for form and database management.

In my opinion, to do it properly, you would need to look into the following (or equivalents), in order to replace your current application:

- Dancer2
- Template(::Toolkit) via Dancer2::Template::TemplateToolkit
- DBI via Dancer2::Plugin::Database
- HTML::FormHandler

And maybe:

- DBIx::Class via Dancer2::Plugin::DBIC

Chris


(This post was edited by Zhris on Aug 19, 2018, 8:36 AM)


Master_Sergius
Novice

Aug 20, 2018, 12:17 AM


Views: 1587
Re: [Zhris] How to write simple API for my cgi script?

Great! Thank you!