http://www.perlmonks.org?node_id=70856

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

Okay, I'm very new to CGI programming in Perl (but definitely not new to programming in general).

I'm doing a very simple shopping cart for a client, but can't seem to grok the coordination between a manually created query string, and being able to access form values.

I dynamically generate a form with these values: <FORM METHOD=POST ACTION="http://CanadaAllergy.com/~canadaal/cgi-bin/shopcart.pl?action=additem">

I'm using a variable called "action" to let my CGI script know that when the submit button of the form is clicked it should branch into its Add Item code.

However, everytime the script runs, and I try debugging with this (these are the first lines of the code):

my $q = new CGI; my $action = $q->param("action") || "enter"; my $cart_id = generate_header($q); my @allnames = $q->param; print "All vars => @allnames<BR>"; print "Action is $action";

I always get this output (along with the rest of the form being generated of course):

All vars => newitem qty Action is enter

So what I want to know is, why doesn't the webserver know that it should keep my "action" variable in there? It appears as though that's getting wiped out and replaced with the two input fields key/value pairs. Or am I taking the wrong approach in the way I'm trying to tell my CGI program what to do to begin with?

Replies are listed 'Best First'.
Re: "Action" variables and form data
by athomason (Curate) on Apr 08, 2001 at 23:07 UTC
    This is a somewhat obscure feature. Using both GET and POST fields is fairly uncommon, and in those instances you might want to keep the two types apart (e.g. to prevent parameter name conflicts). CGI.pm does this separation for you, even if you don't want it to. If you submit both GET and POST parameters, CGI.pm will make only the POST parameters available through its param function. However, it doesn't throw away the GET params, it just makes you insist that you want them.

    To that end, use the url_param function instead of param to fetch the GET values. The two functions work exactly the same way, except you can't set url_params.

    See the MIXING POST AND URL PARAMETERS section of the CGI.pm POD for a bit more info.

Re (tilly) 1: "Action" variables and form data
by tilly (Archbishop) on Apr 08, 2001 at 21:34 UTC
    There are two ways of submitting data to a CGI program, get and post. CGI will transparently switch to the appropriate one, but it does not accept some variables from get and some from post.

    Therefore take the action out of the query string (which is trying to pass it through a get when you are using the post method) and instead add a hidden form element that looks like this:

    <input type="hidden" name="action" value="additem">
Re: "Action" variables and form data
by dws (Chancellor) on Apr 08, 2001 at 21:31 UTC
    Inspect CGI.pm, and search for the line  # Some people want to have their cake and eat it too! Seriously. You'll get some guidance there.

    A more practical workaround might be to add a hidden form field named "action" with the value "additem".

Re: "Action" variables and form data
by kha0z (Scribe) on Apr 09, 2001 at 02:42 UTC
    CGI.pm can be somewhat tricky when you are trying to get the parameters from both GET and POST methods. The url_param instead of param will get the parameters from the url line. However, I have found that CGI.pm can still be somewhat mischivious when trying to get the values from the param function especially when dealing with both the GET and POST methods. So here is my suggestion. Parse the query string yourself. The client will send an environment variable called QUERY_STRING. So try to get it this way: (Assuming you only have ?action=additem in the query string)
    my $query_srting = $ENV{QUERY_SRING}; my ( $query, $action ) = split /=/, $query_string;
    This should put "additem" in your $action . Good Luck!

    Update: Thanks to tilly and Ovid for your suggestions. Seems that I still have a long way to go to get this perl thing down. Anyway, I agree that rolling on your own can be much harder than really understanding how CGI.pm works and forgeting the little details such as the difference between POST and GET. Thanks to you guys I am going back and revising my CGI code. :)

    Ovid wrote:

    but the "roll your own" parsing that kha0z suggested is terribly broken.
    

    Please enlighten me. I don't see where the "broken" part is.

    Thanks.

    Another Update:Thanks again for your help Ovid. :)

    kha0z -- www.kha0z.net

      Yet another example of why not to roll your own.

      First of all you assumed that the query string only has one name/value pair. What if it has more? Use a hash? Well what if there are multiple name/value pairs? What if someone for testing turns a post back into a get to collect a URL and then wonders why their code broke?

      Plus did you consider the poor schmuck who will use your code with form data that has been modified in escaping? At least you warned people that you got the first wrong. But you should also be pointing people to URI::Escape so they can decode the data.

      No, it is far better to do as CGI intentionally does and leave get/post transparent within the CGI so that the developer can easily switch, and just use hidden form elements as they were intended rather than trying to mix and match.

        That is a very good point. And one that, as a newbie, I forgot to point out. However, I may be wrong, but it think that it is always important to remember that in perl "there is more than one way to do it". That said maybe it is not the best but it is a possible solution to an imidiate problem. Anyway, we should alays comment on these fixes so that we can review them later and that others can improve on our code as well.

        Thanks for bringing up that point. :)

        Update:I stand corrected. Throught the examples and arguements that have been presented, I think that I understand, that by trying to parse through the quesry string myself all I am doing is tring to rewrite a function that is already in CGI.pm. This didn't make sense to me at first, until I sat and thought about it. I agree that instead of writing a quick fix to the problem. We should be robust enough to look at the module and understand how it works and use it to its full potential instead of trying to rewrite the API ourselves.

        kha0z -- www.kha0z.net

      You ask why your code is broken, so here goes:

      Your suggestion on how to parse the CGI code reveals a subtle flaw that is, unfortunately, all to common in programming: it's not robust.

      Here's the original FORM tag:

      <FORM METHOD=POST ACTION="http://CanadaAllergy.com/~canadaal/cgi-bin/s +hopcart.pl?action=additem">
      Here's your code to parse it:
      my $query_srting = $ENV{QUERY_STRING}; my ( $query, $action ) = split /=/, $query_string;

      That code is going to work perfectly fine and as long as the format string remains the same, you can test it all day long and not notice problems. However, the following query strings will all break:

      <form method="post" action="http://someserver.com/index.cgi?action=add +item&id=12345"> <form method="post" action="http://someserver.com/index.cgi?action=add +item&action=checkout"> <form method="post" action="http://someserver.com/index.cgi?action=pro +cess r%E9sum%E9">

      All of the above are valid query strings (even the second with duplicate parameter names), but will cause your code to fail, or in the case of the last one, to produce output the programmer may not be testing for ($action eq 'process r%E9sum%E9'). The problem is that while your code works now, the first time some programmer in the future tries to do something unexpected with the query string, your code will fail. Part of our job as programmers is to anticipate issues like this and write code that is robust enough to handle these issues.

      Now, if I may apologize for a couple of shameless plugs, you can read more about this at use CGI or die; and Lesson 2 of my online CGI course.

      Hope this helps!

      Cheers,
      Ovid

      Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: "Action" variables and form data
by geektron (Curate) on Apr 09, 2001 at 04:09 UTC
    i've encountered this before. if you're using the same variable ( 'action' ), but trying to override the values, you have to:
    $q->delete('action'); $q->param( -name => 'action', -value => 'update' );

    CGI.pm does NOT swap the values in the $q object, but APPENDS them. substitute $action for @action, and you'll see what's happening. CGI.pm has silently converted your scalar value to a list value, and 'enter' is always the first in the list.

Re: "Action" variables and form data
by mothra (Hermit) on Apr 10, 2001 at 16:49 UTC
    For the record (and in no small part due to tilly's suggestions in the CB), I chose to simply name my submit buttons (ie. "Add to Cart", "Checkout") all "action" and then check that value in my script.

    Needless to say, everyone's help (and kind responses) have been greatly appreciated. :)

Re: "Action" variables and form data
by $CBAS (Scribe) on Apr 10, 2001 at 01:07 UTC

    I think the client POSTs the FORM without the action=additem variable. You should add a hidden <INPUT> field with NAME=action and VALUE=additem to the FORM.
    I'm not entirely sure though ... maybe it would work with a GET request?

    -CBAS