Reputation: 7595
I'm trying to implement a RESTful API in Perl. My current idea is to simply parse the path_info with a regex then dispatch the request to the appropriate subroutine which will then spit out the JSON, XML or even XHTML for the requested resource.
For example to retrieve info about user 1234 the RESTful client should find it at:
http://example.com/model.pl/users/1234
Below is the skeleton code of my first attempt at implementing a RESTful API:
model.pl :
#!/usr/bin/perl -w
use strict;
use CGI;
my $q = CGI->new();
print $q->header('text/html');
my $restfuluri = $q->path_info;
if ($restfuluri =~ /^\/(questions)\/([1-9]+$)/) { questions($1, $2); }
elsif ($restfuluri =~ /^\/(users)\/([1-9]+$)/) { users($1, $2); }
sub questions
{
my $object = shift;
my $value = shift;
#This is a stub, spits out JSON or XML when implemented.
print $q->p("GET question : $object -> $value");
}
sub users
{
my $object = shift;
my $value = shift;
#This is a stub, spits out JSON or XML when implemented.
print $q->p("GET user: $object -> $value");
}
Before I proceed any further, I would like to hear from experienced Perl hackers whether I got the basic idea right and if there are any serious shortcomings with this approach in terms of performance.
I can imagine, after a while, the if/else block would grow really large.
Looking forward to hear your views to make this code better.
Upvotes: 13
Views: 41955
Reputation: 1073
I know this has been a long time since this question has been asked, but I would like to provide some updated information.
There is a very useful module called Net::API::REST using Apache2 mod_perl behind.
You just need to set up Apache, and create your own module inheriting from Net::API::REST and create an init
method in which you define your endpoints map, such as:
sub init
{
my $self = shift( @_ );
$self->{routes} =
{
# e.g. your API version 1
1 =>
{
'favicon.ico' => $self->curry::noop,
auth =>
{
google =>
{
_handler => $self->curry::oauth_google,
callback => $self->curry::oauth_google(callback => 1),
},
linkedin =>
{
_handler => $self->curry::oauth_linkedin,
callback => $self->curry::oauth_linkedin(callback => 1),
},
},
},
stripe => $self->curry::stripe,
};
$self->{api_version} = 1;
$self->{supported_api_versions} = [qw( 1 )];
$self->{default_methods} = [qw( GET POST )];
$self->{supported_methods} = [qw( DELETE GET HEAD OPTIONS POST PUT )];
$self->{supported_languages} = [qw( en-GB en fr-FR fr ja-JP )];
$self->SUPER::init( @_ );
return( $self );
}
And when someone access one of your endpoint, your corresponding method gets called in the context of a powerful environment to use to reply to the request. Check the module metacpan page for more information.
Upvotes: 0
Reputation: 1
The Simple Solution - with a variable tweak on $data
using qq~~;
Keeps all code lined up.
use CGI;
my $page = new CGI;
if( $ENV{ 'REQUEST_METHOD' } eq 'GET' ){
my $data = qq~
{
"isbn" : "123456",
"title" : "Programming Perl",
"author" : "L. Wall"
}
~;
print $page->header('application/json');
print $data;
}
Upvotes: -1
Reputation: 2603
The simple solution:
use CGI;
my $page = new CGI;
if( $ENV{ 'REQUEST_METHOD' } eq 'GET' ){
my $data = <<json;
{
"isbn" : "123456",
"title" : "Programming Perl",
"author" : "L. Wall"
}
json
print $page->header('application/json');
print $data;
}
Upvotes: 1
Reputation: 1513
For lightweight REST APIs I would look at Mojolicious. The request routing is really straightforward and the inbuilt JSON renderer and user agent make development of simple REST APIs very straightforward in my experience.
If your app is going to be relatively small then Mojo::Lite may suit your requirements. For example you may be able to do something like this:
use Mojolicious::Lite;
get '/questions/(:question_id)' => sub {
my $self = shift;
my $result = {};
# do stuff with $result based on $self->stash('question_id')
return $self->render_json($result)
}
app->start;
Upvotes: 22
Reputation: 50832
Why not use the already implemented module Apache2::REST ? It is all there.
Upvotes: 1
Reputation: 3096
I would use something like CGI::Application::Dispatch, it lets me build a dispatch table with variables and REST methods, and lets you use CGI and CGI::Application modules from CPAN. E.g.:
table => [
'/questions/:id[get]' => { rm => 'get_question' },
'/users/:id[get]' => { rm => 'get_user' }, # OR
':app/:id[post]' => { rm => 'update' }, # where :app is your cgi application module
':app/:id[delete]' => { rm => 'delete' },
],
(or you can use auto_rest or auto_rest_lc)
you can use a separate CGI::Application class for each type of thing (or just use classes in your cgi-app controller class methods).
CGI::Application also comes with plugins for outputting XML, JSON or text generated from templates.
cgi-app (and c::a::d) are are CGI applications and can be used with (little or) no change under CGI, FastCGI or mod_perl. C::A::D is also a mod_perl PerlHandler by default too.
Upvotes: 9
Reputation: 943561
I'd build the application using Catalyst and Catalyst::Controller::REST
Upvotes: 1
Reputation: 96
Why don't you use apache mod_rewrite?
Redirect http://mysite.com/model.pl/users/1234 --> http://mysite.com/users.pl
Redirect http://mysite.com/model.pl/q/5678 --> http://mysite.com/questions.pl
Upvotes: -3