The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

Apache::Request::Dispatcher - dispatches requests to a sub-class of Apache::Request::Controller

SYNOPSIS

    <Location /myApplication>
      SetHandler perl-script
      Perlhander Apache::Request::Dispatcher
      SetEnv DispatcherConf /path/to/file.cfg
      setEnv APP_NAME myApplication
    </Location>

DESCRIPTION

Apache::Request::Dispatcher is a mod_perl handler which handles Apache HTTP requests under mod_perl, and dispatches them to a sub-class of Apache::Request::Controller (after some initial request setup has been performed).

If required, it will establish a connection to a database using the DBI, retrieve (or create) session data for this request (using Apache::Session), initialise a template processor (using the Template-Toolkit).

The Dispatcher parses the URI of the request to determine which subclass of Apache::Request::Controller to then pass control to.

Parsing of the URI occurs as follows:

APP_NAME (environment set in Apache Config) is removed from the begining of the URI, so that:

    '/myApplication/SubClass/action'

becomes: 'SubClass/action'

or

    '/myApplication/Sub/Class/action'

becomes: 'Sub/Class/action'

This is then converted to a module name, and a method name, such as:

    Apache::Request::Controller::SubClass or
    Apache::Request::Controller::Sub::Class

with action() being the method name.

It should be noted that if the SubClass or Action name contain any thing other than [A-Za-z0-9_] then the request is declined.

The dispatcher then dynamically inherits from the module name, and then calls $self->action().

The action() method of the controller is then called in an object-oriented fashion, with a dispatcher object passed in as its first parameter.

This object contains the following hash elements:

    request  => $r,        # The Apache Request Object
    dbh      => $dbh,      # The Database Connection Object
    cfg      => $cfg,      # The AppConfig object
    template => $template, # The Template Processor
    apr      => $q,        # The CGI/libapreq object
    session  => \%session  # Any session data for this user

Depending on the configuration file, 'dbh' or 'session' may be undefined if they've been turned off.

an example controller method might be written as follows:

    package Apache::Request::Controller::SubClass;
    our @ISA = qw(Apache::Request::Controller);
    use strict;
    use Apache::Const qw(:common :methods :http);

    sub action
    {
        my $self = shift;

        my $thing = getThingByID($self->{'dbh'}, $self->{'apr'}->param('thingId'));

        $self->{'request'}->status(HTTP_OK);
        $self->{'request'}->content_type('text/html');

        $self->{'template'}->process('myTemplate', {thing => $thing});
        return OK;
    }
    1;

Special Actions

__cache()

Generally, the controllers that are dispatched to will generate dynamic content, and as such the dispatcher automatically sets the browsers caching policy to not cache any content. However, if your Controller sub-class has a method called __cache() then will be used to define the caching policy. The action name is given as a parameter to the __cache() method, and based on this, the __cache() method should return 1 to allow caching, or zero to prevent it.

For exmaple, if your Controller sub-class provides 2 actions, staticContent() and dynamicContent(), then your __cache() method can control the caching policy as follows:

    sub __cache
    {
        my $action = shift;

        my $policy = { staticContent  => 1,
                       dynamicContent => 0};
        return $policy->{$action} || 0; # Default to no caching.
    }

If your Controller sub-class wants to turn off caching globally, then you can just return zero regardless of what the action name is.

__index()

If the dispatcher cannot work out which action the request is for (this happens on a URI such as '/myApplication/SubClass') then the dispatcher checkes to see if the Controller SubClass has a 'default' action by calling __index(). If this method does not exist, then the request is declined. The __index() method should return the name of the default action, such as:

    sub __index
    {
        return 'listAllItems';
    }

Template Defaults

The template processor has the following defaults defined, and may be used by all templates:

DisplayNumber()

This template variable is a reference to a subroutine which will add comma's in the right place in numbers, for exmaple:

    [% DisplayNumber(1000) %] becomes 1,000
DisplayDate()

This template variable is a reference to a subroutine which will take a time value (in the form of seconds since the epoch) and display the actual date. You may optionally specify an strftime() format:

    [% DisplayDate( CURRENT_TIME, '%a %d %B %Y' ) %]
DisplayTime()

This template variable is a reference to a subroutine which will take a time value (in the form of seconds since the epoch) and displays the time-of-day. You may optionally specify an strftime() format:

    [% DisplayTime( CURRENT_TIME, '%a %d %B %Y' ) %]
DisplayDateTime()

This template variable is a reference to a subroutine which will take a time value (in the form of seconds since the epoch) and displays the date and time of day

    [% DisplayDateTime( CURRENT_TIME ) %]
DisplayDuration()

This template variable is a reference to a subroutine which will take a number of seconds as input, and output a string of the form 'H hours, M minutes and S seconds', eg:

    Last Modified: [% DisplayDuration( CURRENT_TIME - LAST_MODIFIED %]
APP_NAME

This template variable is a string which represents the application name, as defined by the environment variable APP_NAME.

REQUEST

This template variable is an Apache2::RequestRec object, so that the template can have access to the current URI etc. Its not really meant to be used to set any outgoing headers or any thing tho, as setting up the response should really be done in the Controller.

The Configuration File

The dispatcher can be used to dispatch to multiple controllers that dont even need to belong to the same application, and each can application can have its own database connection and set of templates. This is achieved by having Apache specify which configuration file to use based on the Location of the request URI. For example:

    <Location /myApplication>
      SetHandler perl-script
      Perlhander Apache::Request::Dispatcher
      SetEnv DispatcherConf /path/to/file.cfg
      setEnv APP_NAME myApplication
    </Location>

will specify that all requests with a root-URI of /myApplication will use the configuration file as specified in the DispatcherConf environment variable, which is /path/to/file.cfg in the above example.

The contents of this configuration file are as follows:

    db_dsn="DBI:Pg:database=myApplication;host=127.0.0.1"
    db_username=apache
    db_password=apache

    templatePath="/data/myApplication/templates"

    defaultController=WelcomePage

    # If you wish to use sessions, uncomment this and make sure you have
    # created the sessions table within the database db_dsn.
    useSessions="Apache::Session::Postgres"
    sessionTable="sessions"

where db_dsn, db_username, and db_password specifies the database connection options, templatePath specifies where the templates are all stored, and defaultController specifies which controller is the default, so that if a request for '/myApplication' is recieved, it will be converted to '/myApplication/WelcomePage', which will in turn get converted to Apache::Request::Controller::WelcomePage.

At a minimum, you have to have templatePath defined. If you dont specify a default Controller, then the top-level URI will not work.

AUTHOR

Bradley Kite <bradley-cpan@kitefamily.co.uk>

If you wish to email me, then please remove the '-cpan' part of my email address as anything addressed to 'bradley-cpan' is assumed to be spam and is not read.

SEE ALSO

Apache::Request::Controller, DB::Table, DB::Table::Row, DBI, perl

1 POD Error

The following errors were encountered while parsing the POD:

Around line 23:

=pod directives shouldn't be over one line long! Ignoring all 2 lines of content