13. パラメータの受け渡し
今日は、パラメータの受け渡しについてご説明いたします。
■パラメータについて
パラメータとは、一言で言うと、クライアントが送ってくるデータのことです。
ホームページを見るユーザは、最初にURLを指定して、リクエストというものを
送ってきます。
これを受け取ったサーバは、要求されたページをクライアントに対して返します。
これをレスポンスといいます。
よくアンケートフォームなどで名前や住所などを入力して送信すると、リクエストに
名前や住所のデータがくっついてきます。これがパラメータになります。
また、クライアントが送るリクエストは、GETリクエストとPOSTリクエストに分ける
ことができます。
プログラムを作成する人にとって、GET送信とPOST送信の違いは常に認識して
おかなくてはなりませんが、詳しく説明すると長いので、ここでは簡単に説明
します。
ユーザが単純に、このページが見たいというリクエストの場合は、通常、GET送信に
なり、アンケートフォームなど、ユーザが入力したデータを送信する場合は、通常、
POST送信となります。
GETかPOSTかはホームページ作成者が自由に決められますが、ただ単に
<A HREF="・・・">でリンクしたものをクリックした場合は、GETリクエストに
なります。同様に、ブラウザのアドレスバーにURLを直接入力した場合も、GET
リクエストになります。
POSTリクエストを送らせるには、
<FORM ACTION="・・・" METHOD="POST">・・・</FORM>
というように、フォームタグを使用することでPOST送信になります。
GETやPOSTについては、レッスンを進めていくうちに理解していっていただければ
と思います。
■パラメータの渡し方
先にパラメータの渡し方を説明しておきます。
パラメータを渡すには、通常、HTMLの<FORM>タグを使用します。このページは
.htmlの普通のHTMLファイルで構いません。
-----------------------------------------------------------------
<FORM action="test.cgi" method="post">
<INPUT name="text1" type="text" value="test1">
<INPUT name="text2" type="text" value="test2">
<INPUT type="submit" value="送信">
</FORM>
-----------------------------------------------------------------
この場合、POST送信になりますが、<FORM>タグのmethod="get"とすれば、
GET送信になります。
GET送信の場合、テキストボックスに"test1"、"test2"と入力して「送信」ボタンを
押すと、ブラウザのアドレスバーに
http://127.0.0.1/test.cgi?text1=test1&text2=test2
のような感じで、表示されると思います。(ページがなくても構いません)
この、"?"から後ろの文字がパラメータになります。
パラメータは、nameとvalue値に分けることができます。
nameは、<INPUT>タグ内のnameになります。value値は入力した値になります。
アドレスバーは、
(URL)?(name)=(value値)&(name)=(value値)&・・・・
と表示されています。
見て分かる通り、パラメータが複数ある場合は、"&"によって区切られます。
これに対し、POST送信の場合は、ブラウザのアドレスバーにパラメータは
表示されません。
POST送信のメリットは、大量のデータをクライアントから送ってもらうことが
できます。GETの場合は、パラメータの量に制限があります。
■パラメータの受け取り
GETとPOSTでパラメータの受け取り方法が異なります。
パラメータは、環境変数と呼ばれる特殊なハッシュ配列に格納されています。
環境変数というのは、クライアントのOS情報や、ブラウザ情報などのデータや、
要求したURLやファイル名など、クライアントから送られてくる、様々な情報が
格納されている変数になります。
Perlの場合、環境変数は、$ENVという配列から取り出せます。
まず、$ENV{'REQUEST_METHOD'}という変数にGETかPOSTかの情報を取得します。
次に、GETの場合は、$ENV{'QUERY_STRING'}からパラメータを取り出します。
POSTの場合は、$ENV{'CONTENT_LENGTH'}から取り出します。
以下のサンプルを参照してください。
-----------------------------------------------------------------
# パラメータの読み込み
if ($ENV{ "REQUEST_METHOD" } eq "POST") {
#// POSTなら標準入力から読み込む
read(STDIN, $query_string, $ENV{"CONTENT_LENGTH"});
}
else {
# GETなら環境変数から読み込む
$query_string = $ENV{"QUERY_STRING"};
}
-----------------------------------------------------------------
POSTの場合は、read()関数を使用して、標準入力(STDIN)から読み込む形に
なります。
これで、GETでもPOSTでも、変数$query_stringにパラメータの文字列が格納
されます。
この時、変数$query_stringには、
text1=test1&text2=test2
というような文字列が入っているので、さらにこれを分解して 1つずつ取り出す
必要があります。
-----------------------------------------------------------------
# 「変数名1=値1&変数名2=値2」の形式をアンパサンド(&)で分解
@a = split(/&/, $query_string);
# パラメータの取得
foreach $a (@a) {
# =(イコール)で分解
($name, $value) = split(/=/, $a);
# 後で使用するため,$in{'パラメータ名'} に代入しておく
$in{ $name } = $value;
}
-----------------------------------------------------------------
これで、$inのハッシュ配列にすべてのパラメータが格納されます。
パラメータ取得部分については、関数化しておくと便利です。
まとめサンプルで関数化のサンプルをのせておきますので、参照してください。
また、このハッシュ配列の取り出し方は、以下のようにします。
-----------------------------------------------------------------
print $in{'text1'};
print $in{'text2'};
-----------------------------------------------------------------
★今日のまとめサンプルプログラム
index.html
-----------------------------------------------------------------
<HTML>
<BODY>
<FORM action="test.cgi" method="post">
<INPUT name="text1" type="text" value="test1">
<INPUT name="text2" type="text" value="test2">
<INPUT type="submit" value="送信">
</FORM>
</BODY>
</HTML>
-----------------------------------------------------------------
test.cgi
-----------------------------------------------------------------
#! /usr/local/bin/perl
print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<HTML>
<BODY>
END_OF_HTML
%data = GetPara();
print $data{'text1'};
print $data{'text2'};
print << "END_OF_HTML";
</BODY>
</HTML>
END_OF_HTML
#------------------------------------------------------------------
# 関数 : GetPara
# 概要 : パラメータ取得
# 引数 : なし
# 戻値 : パラメータハッシュ配列
#------------------------------------------------------------------
sub GetPara
{
my($query_string); #// エンコードされたパラメータ全体
my(@a, $a); #// エンコードされたパラメータを分解したもの
my($name, $value); #// デコードされたパラメータ
my(%in);
#// パラメータの読み込み
if ($ENV{ "REQUEST_METHOD" } eq "POST")
{
#// POSTなら標準入力から読み込む
read(STDIN, $query_string, $ENV{ "CONTENT_LENGTH"});
}
else
{
#// GETなら環境変数から読み込む
$query_string = $ENV{"QUERY_STRING"};
}
#// 「変数名1=値1&変数名2=値2」の形式をアンパサンド(&)で分解
@a = split(/&/, $query_string);
# パラメータの取得
foreach $a (@a)
{
#// =(イコール)で分解
($name, $value) = split(/=/, $a);
#// + や %8A などのデコード
$value =~ tr/+/ /;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex( $1 ))/eg;
$value =~ s/\t/ /g;
$value =~ s/\r//g;
$value =~ s/\n/<br>/g;
#// 後で使用するため,$in{'パラメータ名'} に代入しておく
$in{ $name } = $value;
}
return %in;
}
exit;
-----------------------------------------------------------------
■解説
パラメータ取得のforeach文の中で、改行やタブなどの文字を安全な文字に変換
しています。
また、半角スペースや"&"など、ブラウザのアドレスバーに使用できない文字は、
エンコードされているので、これをデコードしています。
★課題
1. 以下のテキスト情報をPOSTで受け取って、HTML上に受け取ったデータを表示する
プログラムを作成してください。また、()の条件に合わない場合は、エラー
メッセージを表示するようにしてください。
・名前 (50バイトまで)
・郵便番号 (8バイトまで、半角数字・ハイフンのみ)
・住所 (255バイトまで)
・電話番号 (13バイトまで、半角数字・ハイフンのみ)
・メールアドレス (50バイトまで、"@"と"."が含まれている)
・コメント (255バイトまで)
2. 1の問題で受け取った情報をファイルに追加書き込みしていくプログラムを作成
してください。(カンマ区切り)
★前回の課題の解答
1. 以下の処理を行うプログラムを作成してください。
(1) "test.tmp"ファイルの存在を判別
(2) "test.tmp"が存在する場合は、"エラー"を表示して終了
(3) "test.tmp"に現在時刻を書き込む(yyyy/mm/dd hh:mm:ss)
(4) "test.tmp"のファイルサイズが 0 かどうかを判別
(5) "test.tmp"のファイルサイズを取得
(6) "test.tmp"を削除する
→下のサンプルを参照してください。
-----------------------------------------------------------------
#! /usr/local/bin/perl
print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<HTML>
<BODY>
END_OF_HTML
$filename = "test.tmp";
# ファイルの存在を判別
if (-e $filename) {
print "エラー";
exit;
}
# ファイルオープン
open (FH, "> $filename") || exit;
# 現在時刻を取得
($sec,$min,$hour,$mday,$mon,$year) = localtime(time);
$year += 1900;
$mon++;
print FH sprintf("%04d/%02d/%02d %02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec);
# ファイルクローズ
close(FH);
# ファイルサイズの判別
if (-s $filename) {
# ファイルサイズの取得
@file_info = stat($filename);
print $file_info[7];
}
else { exit(); }
# ファイルの削除
unlink $filename;
print << "END_OF_HTML";
</BODY>
</HTML>
END_OF_HTML
exit;
-----------------------------------------------------------------
■解説
エラー発生時に、exit()のみにしていますが、エラー処理をきちんと行う方が
もちろん良いです。
ファイルハンドラは、必要のなくなった時点で、すぐにクローズするようにして
ください。
このプログラムは、ファイルを作成してすぐに削除していますが、ファイルが
作成されているかを確認するには、unlinkの行をコメントアウトします。
ファイルが存在していると、エラーとなることも確認してください。
2. 前回作成したアクセスカウンタのプログラムを変更し、ファイル書き込み時に
ファイルロックを行うように変更してください。
→下のサンプルを参照してください。
-----------------------------------------------------------------
#! /usr/local/bin/perl
print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<HTML>
<BODY>
END_OF_HTML
$filename = './count.txt';
# ファイル読み込み
if (!open(FH, "< $filename")) {
print "ファイルオープンに失敗しました。($filename)";
exit;
}
# データの取得
$cnt = <FH>;
# ファイル開放
close(FH);
if ($cnt eq '') { $cnt = 0;}
$cnt = $cnt + 1;
# ファイル書き込み
if (!open(FH, ">> $filename")) {
print "ファイルオープンに失敗しました。($filename)";
exit;
}
# ファイルロック
flock(FH, LOCK_EX);
# ファイルの中身を削除
truncate(FH, 0);
# データの書き込み
print FH $cnt;
# ファイルロック解除
flock(FH, LOCK_UN);
# ファイルクローズ
close(FH);
print $cnt;
print << "END_OF_HTML";
</BODY>
</HTML>
END_OF_HTML
exit;
-----------------------------------------------------------------
■解説
ファイルを書き込みモードでオープンするやり方でもOKですが、上記の処理の
方が安全と言えます。
もっと安全やり方でトランザクション管理というものがありますが、これに
ついては、また別の機会にご説明します。
|