📄 ch22.htm
字号:
card_number = 123456789<BR>
city = Beverly Hills<BR>
country = USA<BR>
email = ken.hunt@westbevhigh.edu<BR>
gift = TriCorder<BR>
payment_method = Frontiers Credit Card<BR>
state = CA<BR>
suggestions = Can you offer "Asteroid Living for Kids"
Magazine?<BR>
zines = Cooking With Soylent Green,Asteroid Living<BR>
zipcode = 90210</FONT></TT>
</BLOCKQUOTE>
<H3><A NAME="AcceptingFormsbyMETHODGET">Accepting Forms by <TT><FONT SIZE=4 FACE="Courier">METHOD=GET</FONT></TT></A>
</H3>
<P>
Let's not forget that the codes we have written in the preceding
will work with any set of data posted from a form via <TT><FONT FACE="Courier">METHOD=POST</FONT></TT>.
It's not difficult to adjust our code so that either <TT><FONT FACE="Courier">POST</FONT></TT>
or <TT><FONT FACE="Courier">GET</FONT></TT> can be used.
<P>
Remembering our environment variables, the method being used to
submit the form is stored in the environment variable <TT><FONT FACE="Courier">REQUEST_METHOD</FONT></TT>.
For <TT><FONT FACE="Courier">METHOD=GET</FONT></TT>, the data
passed by the form is displayed in the URL following a <TT><FONT FACE="Courier">?</FONT></TT>,
and the data itself is stored in the environment variable <TT><FONT FACE="Courier">QUERY_STRING</FONT></TT>.
<P>
Putting all of our changes together (see Listing 22.2), we have
a program that can be used to parse the data from any form and
display it to the screen.<BR>
<HR>
<BLOCKQUOTE>
<B>Listing 22.2. A general use form parser.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">#!/usr/bin/perl<BR>
print "Content-type: text/html\n\n";<BR>
<BR>
if ($ENV{'REQUEST_METHOD'} eq "GET") {<BR>
$input = $ENV{'QUERY_STRING'};<BR>
}<BR>
elsif ($ENV{'REQUEST_METHOD'} eq "POST") {<BR>
read(STDIN,$input,$ENV{'CONTENT_LENGTH'});<BR>
}<BR>
else {<BR>
print('Request method Unknown');<BR>
exit;<BR>
}<BR>
<BR>
@input = split (/&/,$input);<BR>
<BR>
foreach $i (0 .. $#input) {<BR>
$input[$i] =~ s/\+/ /g;<BR>
$input[$i] =~ s/%(..)/pack("c",hex($1))/ge;<BR>
($name, $value) = split(/=/,$input[$i],2);<BR>
$input{$name} .= '~' if defined($input{$name});<BR>
$input{$name} .= $value;<BR>
}<BR>
<BR>
print <<EOT;<BR>
<HTML><HEAD><BR>
<TITLE>Order Output</TITLE><BR>
</HEAD><BR>
<BR>
<BODY><BR>
EOT<BR>
<BR>
foreach (sort keys %input) {<BR>
print "$_ = $input{$_}<br>\n";<BR>
}<BR>
<BR>
print <<EOT;<BR>
</BODY><BR>
</HTML</FONT></TT>><BR>
<TT><FONT FACE="Courier">EOT</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Note</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
If we had been using the available cgi-lib.pl library, we could have written a program quite similar to the general form parser and much smaller by writing</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">#!/usr/bin/perl<BR>
require "cgi-lib.pl";<BR>
<BR>
&PrintHeader;<BR>
&ReadParse(*input);<BR>
&HtmlTop("Order Form Output");<BR>
&PrintVariables(% input);<BR>
&HtmlBot</FONT></TT>
</BLOCKQUOTE>
<BLOCKQUOTE>
But we wouldn't have learned very much about Perl. The cgi-lib.pl library is a powerful suite of tools and makes the programmer's job a whole lot easier. Once you have a handle on how Perl programs work, I highly recommend using them. To find out more
about the powerful and ever-growing cgi-lib.pl library, visit the cgi-lib.pl Web site at</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"><A HREF="http://www.bio.com.ac.uk/cgi-lib/">http://www.bio.com.ac.uk/cgi-lib/</A></FONT></TT>
</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<H2><A NAME="CheckingforErrors"><FONT SIZE=5 COLOR=#FF0000>Checking
for Errors</FONT></A></H2>
<P>
At the moment, we still really don't have a processed order form.
All we have is a method of displaying form output on the screen.
What we really need is to get that information off to a human
being who processes orders and collects money.
<P>
In order to save that person time, though, we should make sure
that the information in the order form is as correct and complete
as possible. For instance, you should check to make sure that
all of the fields have been filled in and that the correct number
of digits has been entered for the type of credit card the customer
has indicated. This is the point in the development of this program
where it becomes specific to the form we are parsing. As far as
error checking goes, every form will have different needs.
<P>
In our order form, there are not very many boxes that are optional
information; each box must contain data, except for the <TT><FONT FACE="Courier">card_number</FONT></TT>
box, which needs to be filled in only if the customer is using
a credit card. If it does contain information, it must be in a
specific form.
<P>
First, let's just record any empty data fields. We will check
for empties while creating the associative array. If a field is
empty, we will add the name of the field to an errors array (<TT><FONT FACE="Courier">@errors</FONT></TT>).
<BLOCKQUOTE>
<TT><FONT FACE="Courier">push (@errors,"$name") if $value
eq '';</FONT></TT>
</BLOCKQUOTE>
<P>
Then, if there are errors, we will divert to a subroutine that
will tell the user which information is missing and ask the user
to go back and enter the missing data:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">if ($#errors != -1) {<BR>
&printerrors(*errors)<BR>
}<BR>
else {<BR>
foreach (sort keys %input) {<BR>
print "$_ = $input{$_}<br>\n";<BR>
}<BR>
}<BR>
</FONT></TT>
</BLOCKQUOTE>
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Note</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
Note that if an array is empty, the size of that array, in this case <TT><FONT FACE="Courier">$#errors</FONT></TT>, will be <TT><FONT FACE="Courier">-1</FONT></TT>, not zero. <TT><FONT FACE="Courier">$#<I>array_name</I></FONT></TT> is not a count of the
elements in <TT><FONT FACE="Courier">@<I>array_name</I></FONT></TT> but rather the index of the last element in <TT><FONT FACE="Courier">@<I>array_name</I></FONT></TT>. Because Perl indexes arrays starting at element <TT><FONT FACE="Courier">0</FONT></TT>,
an array with a single element would produce <TT><FONT FACE="Courier">$#<I>array_name</I> = 0</FONT></TT>. Further note that <TT><FONT FACE="Courier">*errors</FONT></TT> is the <TT><FONT FACE="Courier">glob</FONT></TT> of <TT><FONT
FACE="Courier">errors</FONT></TT> variables; therefore, any variable named "errors" whether it be a scalar array or associative array will be included (<TT><FONT FACE="Courier">@errors</FONT></TT>, <TT><FONT FACE="Courier">$errors</FONT></TT>,
<TT><FONT FACE="Courier">$#errors</FONT></TT>, <TT><FONT FACE="Courier">%errors</FONT></TT>, and so on).
</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<P>
This is the <TT><FONT FACE="Courier">printerrors</FONT></TT> subroutine.
The special variable <TT><FONT FACE="Courier">@_</FONT></TT> contains
the variable list sent to the subroutine. <TT><FONT FACE="Courier">local</FONT></TT>
assigns the variables locally. <TT><FONT FACE="Courier">local(*errors)</FONT></TT>
could have been <TT><FONT FACE="Courier">local(*foo)</FONT></TT>
or <TT><FONT FACE="Courier">local(*bar)</FONT></TT> or anything
else as long as the naming and use of local variables is consistent
within this block.
<BLOCKQUOTE>
<TT><FONT FACE="Courier">sub printerrors {<BR>
local (*errors) = @_;<BR>
<BR>
print <<EOT;<BR>
<h2>Your Order could not be processed because the following
<BR>
information was either not supplied or was in an incorrect format.<h2>
<BR>
<b><BR>
EOT<BR>
<BR>
print join('<br>',@errors), "</b><br>\n";
<BR>
print "Please go back and complete the order form.";
<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
Because we are capturing all empty data lines, an error will be
reported if nothing is entered in the <TT><FONT FACE="Courier">card_number</FONT></TT>
cell, even if the customer has indicated payment will be made
by cash or check. Let's check this cell by itself to make sure
that an error isn't reported where one is not and that even if
there is information in the cell, it conforms to the proper format
for the indicated credit card. The method we are currently using
also will not capture unentered data for radio buttons or checkboxes
where none are checked. This is because in these cases, no information,
not even the variable name, has been sent by the form.
<H3><A NAME="EmbeddingInformationintheForm">Embedding Information
in the Form</A><BR>
</H3>
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Tip</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
Here's one of my personal secrets. In order to make things easier on myself when it comes to sorting and storing information, I often embed some information in the form itself.</BLOCKQUOTE>
<BLOCKQUOTE>
This is a trick I have found particularly useful time and time again. Here, in the value field for <TT><FONT FACE="Courier">payment_type</FONT></TT>, I have included the name of the method of payment and the length of the <TT><FONT
FACE="Courier">card_number</FONT></TT>. This makes it much easier to check if the length of the <TT><FONT FACE="Courier">card_number</FONT></TT> field is correct. You can also use this technique to embed information like price, model number, credit limits,
or any other data you might need.
</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"><b>Payment Method:</b><BR>
<br><BR>
<INPUT TYPE="radio" NAME="payment_method"
<BR>
VALUE="FooBar_Charge_Card 8" chECKED>FooBar Charge
Card<br><BR>
<INPUT TYPE="radio" NAME="payment_method"
<BR>
VALUE="Frontiers_Credit_Card 6">Frontiers Credit
Card<br><BR>
<INPUT TYPE="radio" NAME="payment_method"
VALUE="COD 0">C.O.D.<br><BR>
<INPUT TYPE="radio" NAME="payment_method"
VALUE="Check 0">Check<br><BR>
<INPUT TYPE="radio" NAME="payment_method"
<BR>
VALUE="Money_Order 0">Money Order<br></FONT></TT>
</BLOCKQUOTE>
<P>
The information we have embedded in the value for <TT><FONT FACE="Courier">payment_method</FONT></TT>
is parsed out and placed into the variables <TT><FONT FACE="Courier">$method</FONT></TT>
and <TT><FONT FACE="Courier">$number_size.</FONT></TT> Using Perl's
powerful pattern matching by regular expression features (<TT><FONT FACE="Courier">\w+</FONT></TT>)
matches an entire word and (<TT><FONT FACE="Courier">\d</FONT></TT>)
matches a single digit.
<BLOCKQUOTE>
<TT><FONT FACE="Courier">if ($name eq 'card_number') {<BR>
($method, $number_size) = $input{payment_method} =~ /(\w+) (\d)/;
<BR>
if ($value eq '') {<BR>
push (@errors,$name) if ($number_size > 0);<BR>
}<BR>
else {<BR>
push (@errors,$name) if ($number_size = 0);<BR>
push (@errors,$name) if (length($value) != $number_size);<BR>
}<BR>
}<BR>
else {<BR>
push (@errors,$name) if $value eq '';<BR>
}<BR>
<BR>
push (@errors,'zines') unless defined $input{zines};</FONT></TT>
</BLOCKQUOTE>
<H3><A NAME="WhattoDowithAllThisData">What to Do with All This
Data?</A></H3>
<P>
Now that we are reasonably sure we have all the data we need in
a correct form, we have to decide what to do with it. There are
three possible ways to handle the data: display it to the screen,
e-mail it to someone who handles orders, or save the results to
a file. We will do all three.
<P>
This subroutine prints the order information to the screen in
a nice little form letter thanking the customer for their order:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">sub printorder {<BR>
local (*input) = @_;<BR>
print <<EOT;<BR>
<h2>Thank you $input{applicant}.</h2><BR>
The following order has been placed. Thank you for shopping the
Frontier.<BR>
<pre><BR>
<b>Address:</b><BR>
<BR>
<b>$input{applicant}</b><BR>
$input{address}<BR>
$input{city}, $input{state}<BR>
$input{zipcode}<BR>
$input{country}<p><BR>
<b>email:</b> $input{email}<BR>
<BR>
<b>Magazines Ordered:</b> $input{zines}<BR>
<b>Free Gift:</b> $input{gift}<BR>
<b>Payment by:</b> $input{payment_method}, $input{card_number}<p>
<BR>
<BR>
<b>Comments:</b><BR>
$input{suggestions}<br><BR>
<BR>
</pre><BR>
<BR>
<p><BR>
EOT<BR>
<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
This subroutine uses the sendmail feature found on UNIX systems
to e-mail the order form to the person responsible for new orders.
There are two things in particular you should note about this
subroutine.
<P>
The first is that it is necessary to place a backslash (<TT><FONT FACE="Courier">\</FONT></TT>)
before the <TT><FONT FACE="Courier">@</FONT></TT> symbol in the
e-mail address of the recipient because Perl interprets <TT><FONT FACE="Courier">@</FONT></TT>
as the beginning of an array variable.
<P>
The second is the method by which sendmail is called. The method
used here pipes output into the sendmail program directly. For
security reasons, this is greatly preferable to calling sendmail
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -