http://qs321.pair.com?node_id=748939

c4onastick has asked for the wisdom of the Perl Monks concerning the following question:

I haven't done much web development in Perl as of yet (I came to Perl from PHP to get away from the web), but decided to get my feet wet a bit with a new project at work. I stumbled across CGI::Application the other day while getting started and thought it'd be great for a rapid development framework (yet another reason I came to Perl from PHP: CPAN! Back in my PHP days I ended "rolling my own" pseudo-CGI::Application for PHP.)

CGI::Application's interface is great (much better than mine!), it gives you a nice MVC canvas to start painting your site upon. However, I did run into a bit of a problem trying to pass variables via both GET and POST, which is typically how I've handled form submissions before (i.e. use the form method "POST" and an action of something like "index.cgi?page=submit_form")

Here's a simplified version of what I've got so far:

File: TestSite.pm

package TestSite; use base 'CGI::Application'; use warnings; use strict; sub setup { my $self = shift; $self->start_mode('mode1'); $self->mode_param('rm'); $self->run_modes( 'mode1' => 'one', 'mode2' => 'two' ); } sub one { my $self = shift; my $template = $self->load_tmpl; return $template->output; } sub two { my $self = shift; my $q = $self->query(); my $template = $self->load_tmpl; $template->param(test_post => $q->param('test_post')); return $template->output; } 1;

With templates that look like:

File: mode1.html

<html> <head> <title>One</title> </head> <body> <h1>Page one</h1> <form action='index.pl?rm=mode2' method='post'> <p>Input: <input type='text' name='test_post' size='20' /> +</p> <p><input type='submit' value='Submit' /></p> </form> </body> </html>

File: mode2.html

<html> <head> <title>Two</title> </head> <body> <h1>Page two</h1> <p>Value of &quot;test_post&quot; is <tmpl_var name='test_post +'>.</p> <p><a href='index.pl?rm=mode1'>Click to go back to one</a></p> </body> </html>

When testing this locally on Apache 2.2 (in Win32), CGI::Application seems to choke, it would make the switch to run mode "mode2" (the address bar would have "rm=mode2") but it would still display "mode1" (the form page), which makes me think it caught some exception and defaulted to "mode1". Unfortunately, I wasn't able to find anything interesting in the Apache logs. I tried a number of things to try to manually debug but with little luck.

Changing to 100% POST did work as expected though...

File: mode1.html

<html> <head> <title>One</title> </head> <body> <h1>Page one</h1> <form action='index.pl' method='post'> <input type='hidden' name='rm' value='mode2' /> <p>Input: <input type='text' name='test_post' size='20' /> +</p> <p><input type='submit' value='Submit' /></p> </form> </body> </html>

So, that leads to my question: How do you handle both POST and GET with CGI::Application?
Or is there some good reason why I shouldn't be mixing POST and GET?
(And thirdly, any tips for debugging CGI::Application? Tried "dump" and "dump_html" but as seen above my problem caused it to die before it got to "dump")

I did some looking through CGI.pm and it looks like it handles POST and GET via two different methods: "param" (for POST) and "url_param" (for GET). "url_param" doesn't appear anywhere in CGI::Application and I was able to isolate CGI::Application as the source by writing it "old-school". The following works as expected:

File: one.pl

#!/Perl/bin/perl use warnings; use strict; use CGI; use HTML::Template; my $q = new CGI; my $template = HTML::Template->new(filename => 'mode1a.html'); print "Content-Type: text/html\n\n", $template->output;

File: two.pl

#!/Perl/bin/perl use warnings; use strict; use CGI; use HTML::Template; my $q = new CGI; my $template = HTML::Template->new(filename => 'mode2a.html'); $template->param(test_post => $q->param('test_post')); print "Content-Type: text/html\n\n", $template->output;

File: mode1a.html

<html> <head> <title>One</title> </head> <body> <h1>Page one</h1> <form action='two.pl?some_var=foo&other_var=bar' method='post' +> <p>Input: <input type='text' name='test_post' size='20' /> +</p> <p><input type='submit' value='Submit' /></p> </form> </body> </html>

File: mode2a.html

<html> <head> <title>Two</title> </head> <body> <h1>Page two</h1> <p>Value of &quot;test_post&quot; is <b><tmpl_var name='test_p +ost'></b>.</p> <p><a href='one.pl'>Click to go back to one</a></p> </body> </html>

Thanks in advance for your help!

Update: I'm using:

Just through CGI, no mod_perl yet.

Replies are listed 'Best First'.
Re: Mixing POST and GET with CGI::Application
by samtregar (Abbot) on Mar 06, 2009 at 19:34 UTC
    I can think of one good reason not to mix POST and GET - it often doesn't work. With the right version of CGI.pm or Apache::Request (depending on whether you're in mod_perl or not) it can work. But what's the upside? Using hidden inputs in a post form is easy and very reliable.

    -sam

      I can think of one good reason not to mix POST and GET - it often doesn't work[!]

      Good point! I was a bit weary of that, but the second set of scripts I have up there does prove that they can coexist peacefully.

      I had intended to use them both for separation of run mode control and transfer of data, i.e. use the GET variables only for "out" functions like: display page X. Then use the POST variables for input, which (in most cases) would be parsed and inserted into a database. Plus the forms I have for the full project will have some pretty hefty data entry, so I might be brushing up against the GET size limit.

Re: Mixing POST and GET with CGI::Application
by jaldhar (Vicar) on Mar 07, 2009 at 09:32 UTC

    CGI::Application uses CGI.pm behind the scenes so if you don't find something in the docs look there as well. In particular I think the section on MIXING POST AND URL PARAMETERS is what you need.

    Based on that information, you can write a function like this:

    # Called before CGI::App dispatches to a runmode sub cgiapp_prerun { my ($self) = @_; # $self->mode_param so we don't have to go back and change this if + we # ever decide to use something other than rm if ($self->query->url_param($self->mode_param)) { # prerun_mode lets you change CGI::Apps notion of the current +runmode $self->prerun_mode($self->query->url_param($self->mode_param)) +; } return; }

    Actually thats kind of overkill. A simpler solution would be to remove rm=mode2 from the forms action and include a hidden field called 'rm' with value 'mode2' in the form body instead.

    Also for debugging look into CGI::Application::Plugin::DebugScreen and CGI::Application::Plugin::DevPopup.

    --
    જલધર

Re: Mixing POST and GET with CGI::Application
by wfsp (Abbot) on Mar 07, 2009 at 09:17 UTC
    I use path_info to pass the run mode when POSTing to a CGI::App.

    In mode1.html the form tag would look like

    <form action='/perl/index.cgi/mode2' method='post'>
    Adjust the path to suit.

    In TestSite.pm you would then have

    $self->mode_param( path_info => 1, param => q{rm}, );
    See the C::A docs for a discussion on path info.
Re: Mixing POST and GET with CGI::Application
by bradcathey (Prior) on Mar 07, 2009 at 04:57 UTC

    I do lots of Web development, and while there are some snazzy newer frameworks out there, I use CGI::Application exclusively and come up with my own "framework" using C::A as a base. Take a look at the plugins, they're big code/time savers. C::A works beautifully with HTML::Template.

    Shameless plug, but seriously, this might help you get acclimated. It's based on lots of experimenting.

    —Brad
    "The important work of moving the world forward does not wait to be done by perfect men." George Eliot