Apache2-Controller
view release on metacpan or search on metacpan
lib/Apache2/Controller.pm view on Meta::CPAN
Apache2::Controller
Apache2::Request
);
use Apache2::Const -compile => qw( :http );
sub allowed_methods {qw( default bar baz )}
# suppose '/foo' is the uri path dispatched to this controller
# and your dispatch uses Apache2::Controller::Dispatch::Simple
# http://myapp.xyz/foo/
sub default {
my ($self) = @_;
$self->content_type('text/plain');
$self->print("Hello, world!\n");
return Apache2::Const::HTTP_OK;
}
# http://myapp.xyz/foo/bar/biz/schnozz
sub bar {
my ($self, @path_args) = @_;
# @path_args is:
# qw( biz schnozz )
# @{ $self->{path_args} }
# @{ $self->pnotes->{a2c}{path_args} }
$self->content_type('text/html');
$self->print(q{ <p>"WE ARE ALL KOSH"</p> });
return Apache2::Const::HTTP_OK;
}
# http://myapp.xyz/foo/baz
sub baz {
my ($self) = @_;
return Apache2::Const::HTTP_BAD_REQUEST
if $self->param('goo'); # inherits Apache2::Request
return Apache2::Const::HTTP_FORBIDDEN
if $self->param('boz') ne 'noz';
$self->content_type('text/plain'); # inherits Apache2::RequestRec
$self->sendfile('/etc/passwd'); # inherits Apache2::RequestIO
return Apache2::Const::HTTP_OK;
}
1;
You could implement a pretty nice REST interface, or any other kind
of HTTP-based API, by returning the appropriate HTTP status codes.
See L<Apache2::Controller::Refcard/status> for a list.
See L<Apache2::Controller::Render::Template> for an additional base
for your controller class to render HTML with L<Template> Toolkit,
auto-selecting a template from the include path based on the
request URI.
=head1 DESCRIPTION
Apache2::Controller is a lightweight controller framework for
object-oriented applications designed to run only under mod_perl
children in high-performance Apache2 handler modules. It features URL
dispatch with flexible configuration, auth plugins, a cookie tracker
for Apache::Session, liberty for any storage models that work under mod_perl,
rendering using Template Toolkit or direct printing with Apache or whatever
you want,
and base inheritance configuration allowing you to
construct your applications as you need, without trying to be all things
to all people or assimilate the world.
It is intended as a framework for
new applications specialized as Apache2 handlers, not as a means to
absorb existing applications or to create portable code.
Apache2::Controller instantiates the L<Apache2::Request>
object and puts it in C<< $self->{r} >>. If you want access
to the methods directly via C<< $self >>, simply use
L<Apache2::Request> as a base and it will auto-delegate
all the methods.
See L<Apache2::Request/SUBCLASSING Apache2::Request>.
For using other Apache2::Controller extension methods, use
another base class like
L<Apache2::Controller::Render::Template>, provides an easy way to
use Template Toolkit by default to render pages, selecting templates
from a directory structure that corresponds to your controller URI's.
Individual controller methods can specify plain text or other
content types and print directly through inherited L<Apache2::RequestIO>
methods, if you use L<Apache2::Request> as a second base and suck
its methods in through that module's auto-magic.
See L<Apache2::Request/SUBCLASSING Apache2::Request>.
Instead of abstracting Rube Goldberg devices around the Apache2 mod_perl
methods, it stays out
of your way and lets you use any and all of them directly through
C<$self> as you see fit, if you use L<Apache2::Request> as a second base.
But you don't have to do that, if you don't want potential namespace
conflicts with your uri's. For example, if you do use
L<Apache2::Request> as a base, you couldn't have a uri 'params'
or 'connection', for example, if you want to be able to use
those Apache2 family methods. The L<Apache2::Request> object
is always in C<< $self->{r} >> either way.
Use L<Apache2::Controller::Dispatch> from your Apache2 config file to
send various URI requests to your page view modules. See the
CONFIGURATION section below. This features a standard mechanism for uri
dispatch in L<Apache2::Controller::Dispatch::Simple> that does not try
to figure out what modules are available, but
simply requires you to provide a hash that maps from uri paths to
controller modules. Or, dispatch plugins can be created to implement
the dispatcher's find_controller() method in some other way, like
with a TRIE for big sites or using other algorithms,
even dynamic ones based on context from the request.
L<Apache2::Controller> is the base module for each controller module.
Depending on your dispatch mechanism, controller modules usually
contain a list of the method names which
are allowed as uri paths under the controller.
=head1 DISPATCH OF URI TO CONTROLLER
You do not put Apache2::Controller or your subclass into the
Apache2 server configuration. Instead you make a subclass
of L<Apache2::Controller::Dispatch> and use that as a
PerlInitHandler. It will map a URI to an appropriate
Apache2::Controller subclass object and method and will
use C<< $r->push_handlers() >> if successful to push Apache2::Controller
onto the modperl response handler stack, which then creates
the right handler object of your subclass and sends the
request to the right method, handling errors in a nice way.
lib/Apache2/Controller.pm view on Meta::CPAN
Some simple types are bundled which depend on
the C<< allowed_methods() >> subroutine in your controller,
but that isn't a required feature -
you can also implement your own dispatch subclass which
does things your way, moves allowed uris around depending
on context in the request, or whatever.
=head1 OTHER REQUEST PHASE HANDLERS
Configure other handlers in your config file to set things up
before your Apache2::Controller runs.
Most of these handlers use L<Apache2::Controller::NonResponseBase>
as a base for the object, which usually does not need to
instantiate the L<Apache2::Request> object, because they
usually run before the response phase, so you usually don't
want to parse and cache the body if you want to use input filters.
If your subclass methods of non-response Apache2::Controller
components need access to the L<Apache2::RequestRec> object C<< $r >>,
it is always in C<< $self->{r} >>.
Some other request phase handlers register later-stage handlers,
for example to save the session or rollback uncommitted database
transactions with C<PerlLogHandler>'s
after the connection output is complete.
The controller handler returns your set HTTP status code
or OK (0) to Apache. In general you return the status code
that you want to set, or return OK. Or you can set it with
C<< $r->status() >> and return OK.
You can't return DONE or DECLINED. (If you find you need
to do that for some reason please contact me.)
See L<Apache2::Const/:http>
and L<Apache2::Controller::Refcard>. You can also set
C<< status_line() >> or throw L<Apache2::Controller::X>
exceptions to be processed by an error template,
if you're using some form of template rendering - see
the section on errors below.
Add handlers in your config file with your own modules which
C<use base> to inherit from these classes as you need them:
=head2 PerlHeaderParserHandler Apache2::Controller::Session
C<< $r->pnotes->{a2c}{session} >> automatically loaded from and
stored to an L<Apache::Session> tied hash. Pushes a PerlLogHandler
to save the session after the main controller returns OK.
See L<Apache2::Controller::Session>
and L<Apache2::Controller::Session::Cookie>.
=head2 PerlAuthenHandler Apache2::Controller::Authen::OpenID
Implements OpenID logins and redirects to your specified login
controller by changing the dispatch selection on the fly.
See L<Apache2::Controller::Authen::OpenID>.
As for Access and Authz phases of AAA, you should
probably roll your own. This framework isn't going
to dictate the means of your data storage or how
you organize your users. See the mod_perl manual.
=head1 Apache2::Controller response phase handler
Apache2::Controller is set as the PerlResponseHandler if
the dispatch class finds a valid module and method for the request.
=head2 Subclass L<Apache2::Request>
Most of the time you will want to use Apache2::Request as
a second base. If you do this, then your controller
inherits the L<Apache2::RequestRec> methods with
(some) modperl2 request extension libraries loaded during
construction, or you can use others in the package namespace
and automatically get the methods.
This way, you can call C<< $self->$methodname >> for any of
the methods associated with L<Apache2::Request>,
L<Apache2::RequestRec> and some of their friends.
Watch the log for warnings about redefined subroutines, or
C<< use warnings FATAL => 'all' >> to keep yourself on the
right track.
To use a simplified example:
package MyApp::C::SomeURIController;
use base qw(
Apache2::Controller
Apache2::Request
);
my %pats = (
shipto => qr{ \A (.*?) \z }mxs,
addr => qr{ \A (.*?) \z }mxs,
zip => qr{ \A (\d{5}) \z }mxs,
);
sub set_shipping_address {
my ($self) = @_;
# $self->param() is Apache::Request param():
my ($shipto, $addr, $zip)
= map {
$self->param($_) =~ $pats{$_};
$1 || return Apache2::Const::SERVER_ERROR;
} qw( shipto addr zip );
$self->content_type('text/plain');
$self->print('Your package is on its way.');
return Apache2::Const::HTTP_OK
}
At any rate, your Apache2::Controller child object
normally subclasses
itself into Apache2::Request which magically
delegates all those methods
to the internal hash value C<< $self->{r} >>, which is the actual
Apache2::Request object.
See L<Apache2::Request/SUBCLASSING Apache2::Request>
about those gory details.
lib/Apache2/Controller.pm view on Meta::CPAN
package MyApp::Cleanup;
sub cleanup {
my ($self) = @_;
# ...
}
package MyApp::C::Foo;
use base qw( Apache2::Controller Apache2::Request MyApp::Cleanup );
sub allowed_methods {qw( foo bar )}
sub foo {
# ...
$self->cleanup();
return;
}
# or...
sub bar {
# ...
$self->push_handler(PerlCleanupHandler => sub {
# ...
});
return;
}
Or better yet...
package MyApp::Cleanup;
sub DESTROY {
my ($self) = @_;
# ...
}
package MyApp::C::Foo;
use base qw( Apache2::Controller MyApp::Cleanup );
sub allowed_methods {qw( foo bar )}
sub foo {
# ...
return;
}
sub bar {
# ...
return;
}
There is no need for a predefined method sequence that
tries to run for each request, because Apache2 already
provides a robust abstraction of the request lifecycle
with many stages for which you can register handler subroutines.
If you can wrap your head around it, inheritance provides many
solutions to problems for which elaborate measures are commonly
re-invented. For example if you wanted cleanup done the same way every
time without having to remember that C<< $self->cleanup() >> line
for each new
method, overload the constructor as per L<subclassing a2c_new( )> above
and register a PerlCleanupHandler for every request instead,
or use a base with a DESTROY method.
Otherwise the framework ends up doing a lot of work every time
to ask, "did they implement this? did they implement that?"
and that gets in your way, or you have to write those routines
every time even if they don't do anything, or whatever. Bleah.
Implement what you want to implement from the controller methods.
The framework won't provide you with any more structure.
=head1 EXAMPLES
Browse the source package from CPAN
and check out t/lib/* and t/conf/extra.conf.last.in.
=head1 RELATED MODULES
L<Apache2::Controller::Directives>
L<Apache2::Controller::Methods>
L<Apache2::Controller::X>
L<Apache2::Controller::Dispatch>
L<Apache2::Controller::Session>
L<Apache2::Controller::DBI::Connector>
L<Apache2::Controller::Auth::OpenID>
L<Apache2::Controller::Refcard>
L<Apache2::Controller::Funk>
=head1 SEE ALSO
L<Apache2::RequestRec> and friends
L<Apache2::Request>
L<Apache2::AuthenOpenID>
L<http://perl.apache.org>
L<http://perl.apache.org/docs/2.0/user/handlers/http.html>
=head1 THANKS
Many thanks to David Ihnen, Adam Prime, André Warnier
and all the great people on the modperl mailing list.
Special thanks to Nobuo Danjou for Apache2::AuthenOpenID
which edumacated me on how the OpenID authen module
should work.
Super thanks to Roberto C. Sánchez for help packaging
up A2C for Debian distribution.
Of course, thanks to the many mod_perl and Apache authors
and all the CPAN authors whose modules this depends on.
Wow! This stuff is so cool!
=head1 AUTHOR
Mark Hedges, C<hedges +(a t)- formdata.biz>
=head1 COPYRIGHT AND LICENSE
( run in 0.889 second using v1.01-cache-2.11-cpan-df04353d9ac )