Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Getting query string with CGI::Fast

by Anonymous Monk
on Jul 28, 2020 at 01:36 UTC ( #11119873=perlquestion: print w/replies, xml ) Need Help??

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

Hi monks,

I'm trying to use FCGI with CGI::Fast to get the params from the url. My fcgi script is named test.fcgi and I tried accessing it from my browser as test.fcgi?node=appy&action=do_something.

While I'm able to get the value of 'node' in the variable $query, the code doesn't seem to call do_something() that comes from the module Myapp.

####start of script test.fcgi #!/usr/bin/perl -T use lib '/path_to_modules/perl5/lib/perl5'; use lib '/path_to_modules/modules'; use FCGI; use CGI::Fast; use Myapp qw(appy); my $n = 0; my %nodes = ( appy => \&appy, _err => \&error, ); while ( my $q = new CGI::Fast ) { my $query = $q->param('node'); print "Content-type: text/plain\n\n"; ++$n; print "You are request number $n. Have a good day!\n"; print "query: $query\n"; $nodes{ exists $nodes{$query} ? $query : '_err' }(); print "done!\n"; } sub error { print "An error occurred.\n"; } ####end of script test.fcgi ####Myapp module package Myapp; use strict; $|++; use Exporter qw(import); our @EXPORT = qw(appy); use CGI; my $q = CGI->new(); sub appy { my $action = $q->param('action') || 'bailout'; my %actions = ( do_something => \&do_something, ); $actions{ exists $actions{$action} ? $action : '_err' }(); } #---------------------------------- # subroutine do_something #---------------------------------- sub do_something { print "do something called\n"; } ####End Myapp module

Am I doing things correctly?

Replies are listed 'Best First'.
Re: Getting query string with CGI::Fast
by hippo (Chancellor) on Jul 28, 2020 at 09:05 UTC
    Am I doing things correctly?

    Not entirely, I suspect. You have 2 different CGI objects: 1 in test.cgi and 1 in Myapp. That seems inherently incorrect. Take the object you already have in test.cgi and pass it to the Myapp subs if that's what you want. Alternatively you could have Myapp subclass CGI::Fast and then just use that in test.cgi and call methods on that object from there. Or if you don't want to subclass CGI::Fast you could have a separate object of Myapp on which to call methods. TIMTOWTDI.

    Here's a simplified example using that last of these approaches:

    #!/usr/bin/perl -T package Myapp; use strict; use warnings; use Try::Tiny; sub new { my ($class, $q) = @_; return bless {q => $q}, $class; } sub appy { my $self = shift; my $action = $self->{q}->param('action') || 'bailout'; try { $self->$action; } catch { print "Unrecognized action : $action\n"; } } sub do_something { print "do something called\n"; } package main; use strict; use warnings; use CGI::Fast; use Try::Tiny; my $n = 0; while (my $q = CGI::Fast->new) { my $query = $q->param('node'); print "Content-type: text/plain\n\n"; ++$n; print "You are request number $n. Have a good day!\n"; print "query: $query\n"; my $app = Myapp->new ($q); try { $app->$query } catch { error() }; print "done!\n"; } sub error { print "An error occurred.\n"; }

    Note that I've combined the module and script into one file for ease of this illustration (it's an SSCCE, albeit you need an FCGI-capable webserver to test it). If you want to split them out again, just add use Myapp; back into the script. I've added Try::Tiny to catch the missing methods rather than bother with dispatch tables just to show another way of doing this but there's nothing wrong with dispatch tables if you prefer them. Equally you could just use UNIVERSAL::can to test the method's existence - so many options! I've used strict and warnings everywhere and so should you.

    To answer the other avenue of discussion, the while loop is absolutely necessary otherwise you will not have the persistence (ie. $n will never increase). Have a think for yourself about why this is.


    🦛

      Thank you so much for your explanation on the "while" vs "if" condition and for your beautiful code - as someone who writes procedural code, I never quite get round to switching to object-oriented. Will try to learn (and adapt) from yours.

      I have a question about persistence. Suppose the module Myapp uses other modules, are these other modules also stored in memory so they are not loaded for every request?

        Suppose the module Myapp uses other modules, are these other modules also stored in memory so they are not loaded for every request?

        Short answer: yes.

        Any module which a script uses is loaded into memory at compile time, including all the modules used by those modules and so on. This memory stays used until the script exits. For long-running scripts such as those under FCGI this means that all those modules remain in memory for a long time. Each web request to your FCGI script will either encounter an existing FCGI process where all those modules are ready and therefore have no startup penalty or occasionally will have to spin up a fresh FCGI process which will take longer because it loads all the modules.

        You can manage this somewhat by loading modules on demand at run-time instead of at compile time. Suppose there's a task your script will need to perform only once in every 10,000 runs and which needs a heavy module. You would not necessarily want that module taking up lots of RAM unnecessarily for 9,999 runs when it isn't needed. This sort of memory and process management is something you will probably want to consider if you are going to use FCGI (or any other persistent back-end) for serious purposes.


        🦛

Re: Getting query string with CGI::Fast
by perlfan (Priest) on Jul 28, 2020 at 03:26 UTC
    This is the ideal case to add:
    use strict; use warnings;
    Also, I think you want an if, not a while (strict/warnings would not have caught the infinite loop tho):
    while ( my $q = new CGI::Fast ) { my $query = $q->param('node'); print "Content-type: text/plain\n\n"; ++$n; print "You are request number $n. Have a good day!\n"; print "query: $query\n"; $nodes{ exists $nodes{$query} ? $query : '_err' }(); print "done!\n"; }
    Update:

    You're not actually calling do_something in any execution path. And you don't need to use CGI in your package just to get the query option. You should be able to get it in the call to CGI::Fast and pass it to a method in your package, assuming you actually call it.

    Maybe nodes{ exists $nodes{$query} ? $query : '_err' }(); needs to be nodes{ exists $nodes{$query} ? $query : '_err' }->(); ... sorry missed that it was a real hash.

    Yeah you got a lot going on. Simplify it. Get rid of the package stuff, make sure your dispatching is clear, and make sure one of your terminal methods actually terminates. Hard to tell at first glance, but I still think your while is causing you some grief.

      Also, I think you want an if, not a while

      No, that's how you're supposed to use CGI::Fast, as a glance into the documentation would have told you. The whole point of FCGI is a persistent process.

      strict/warnings would not have caught the infinite loop tho

      It would?

      (Somehow I missed the "not" in that sentence, my apologies.)
      Maybe nodes{ exists $nodes{$query} ? $query : '_err' }(); needs to be nodes{ exists $nodes{$query} ? $query : '_err' }->();

      No.

      hippo's answer is correct: The problem is there are two CGI objects in the program.

      Thanks for pointers. I see most examples on the Internet use "while", and I'm not really sure why. Would "if" work too?

        Try it without the while loop and see what happens

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://11119873]
Approved by kcott
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2020-08-15 11:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Which rocket would you take to Mars?










    Results (78 votes). Check out past polls.

    Notices?