http://www.perlmonks.org?node_id=387106
Category: CGI Programming
Author/Contact Info Jaldhar H. Vyas <jaldhar@braincells.com>
Description:

While a traditional CGI program is a good way for users to interact with your website, they include a lot of extra interface simply to accomodate human frailties. A web service on the other hand is designed to provide programs with access to the functionality of your site with as little overhead as possible.

You don't have to choose between the two. In this example I show you how to implement both CGI and XML-RPC access to a single script. It uses Frontier::RPC HTML::Template and CGI::Application and provides the ability to get the square of a number you input. It has four files:

  • An XML-RPC client you can run from the command line.
  • A subclass of CGI::Application called Square.pm
  • A driver script called index.cgi which uses Square.pm
  • An HTML::Template called view used by Square.pm

Put these files into a subdirectory called square off the root directory of your webserver. You can call the script as a CGI by accessing http://localhost/square/index.cgi or as an XML-RPC based web service by using the client program.

This is the XML-RPC client.

#!/usr/bin/perl
use strict;
use warnings;
use Frontier::Client;

my $number;

{
  print "Enter a number\n";
  $number = <STDIN>;
  chomp($number);
  redo unless $number =~ /^\d+$/;
}

my $server_url = 'http://localhost/square/index.cgi/RPC2';
my $server = Frontier::Client->new(url => $server_url);

my $result = $server->call('sample.square', $number);
my $square = $result->{'square'};
my $difference = $result->{'difference'};

print "The square of $number is $square.\n";

This is the CGI::Application subclass which implements the functionality of the server.

package Square;
use base 'CGI::Application';

sub setup
{
  my ($self) = @_;

  $ENV{'PATH'} = '';

  my @pi = split(m{/}, $self->query->path_info());
  $self->start_mode(($pi[1] eq 'RPC2') ? 'RPC2' : 'view');
  $self->run_modes(
    'view'   => 'view',
    'RPC2'   => 'rpc2',
  );
}

sub view
{
  my ($self) = @_;

  my $number = $self->query->param('number');
  my $template = $self->load_tmpl('view');
  $template->param(NUMBER => $number,
                   SQUARE => square($number)->{square},
  );
  return $template->output;
}

sub rpc2
{
  my ($self) = @_;

  require Frontier::RPC2;

  my $rpc = Frontier::RPC2->new();
  my $response = $rpc->serve($self->query->param('POSTDATA'),
    {
      'sample.square' => \&square,
    },
  );

  $self->header_props( -type => 'text/xml', charset => 'UTF-8', );
  return $response;
}

sub square
{
  my ($x) = @_;

  return {square => ($x ** 2)};
}

1;

Here is the driver script that uses the above module:

#!/usr/bin/perl -T
use warnings;
use strict;
use lib '.';
use Square;

my $square = Square->new();
$square->run();

Here is the HTML::Template used in the view run mode.

<html>
  <head>
    <title>Square Two Numbers</title>
  </head>
  <body>
    <h1>Square Two Numbers</h1>
    <form action="http://localhost/square/index.cgi/view" method="POST
+">
    Enter a number: <input type="text" name="number">
    <input type="submit">
    </form>
    <!-- TMPL_IF NAME="number" -->
       <p>The square of <!-- TMPL_VAR NAME="number" --> is <!-- TMPL_V
+AR NAME="square" -->.</p>
    <!-- /TMPL_IF -->
  </body>
</html>