入力フォーム編

GET と POST データを受け取るには

CGI を作成する上で、テキストボックスやラジオボタンなどのフォームに入力、選択された状態を取り込むことは避けてとおれません。 ここでは、入力フォームのデータをどのように CGI で取り込むのかを紹介します。 初心者向けに冒頭でで GET と POST についても解説します。

目次

HTTP メソッド

フォームデータをサーバーに送信する方法 (メソッド) には GET メソッドと POST メソッドがあります。 HTTP プロトコルでは、他に PUT メソッドや DELETE メソッドも規定されていますが、 ブラウザーで利用できるのは GET メソッドと POST メソッドの 2 つだけです。

GET メソッドやは、HTTP/0.9 から規定されており、当時ではデータをサーバーに送信する唯一の手段でした。 今でもデータ送信手段として使われていますが、送信データが URL としてブラウザーのアドレスバーに表示されます。 このように URL の一部になってしまう都合上、ブラウザーによって文字長に制限があります。 今はすでに使われなくなりましたが、一時期は最大のシェアを誇った Microsoft Internet Explorer では URL の最大長が 2,048 文字でした。 しかも、URL エンコード (後述) した状態で 255 文字までですので、大きなデータを送信するには限界がありました。

さらに、送信データが URL の一部になるということは、ウェブサーバーのログにもそのまま記録されることになります。 検索キーワード程度ならさほど問題はないでしょうが、個人情報や決済情報などがログにそのまま記録されるのは良いことではありません。

これを解決するのが、最大有効文字数という制限がない POST メソッドです。 送信データは URL にセットするのではなく、メッセージボディとして送信されますので、 利用者の目に直接触れることはありませんし、ウェブサーバーのアクセスログに直接記録されることもありません。

GET は何かしらの情報を引き出すための手段であり、 何かしらのデータを登録したりアップロードしたりするためのメソッドではありません。 GET と POST はその名前が表す通りの目的で使うようにしましょう。

GET メソッドのデータの受け取り方

GET メソッドでは、リクエスト URL の後ろに送信データを付け加える形で送信されます。 CGI 側では、その送信データは環境変数 ENV{QUERY_STRING} に収められます。 次の例をご覧ください。

<form action="https://www.futomi.com/lecture/form/exec/get-post-test.cgi" method="get">
  <p>ニックネーム:<input type="text" name="nickname" value="たろう"></p>
  <p>性別:
    <input type="radio" name="gender" value="male" checked> 男性
    <input type="radio" name="gender" value="female"> 女性
  </p>
  <input type="submit" value="送信">
</form>

ニックネーム:

性別: 男性 女性

上の例では、送信ボタンを押すと $ENV{QUERY_STRING} の値をそのまま表示します。 ここで注意してほしいのが、ブラウザーのアドレス欄に表示されている URL です。 次のようになっているはずです。

https://www.futomi.com/lecture/form/exec/get-test.cgi?nickname=%25E3%2581%259F%25E3%2582%258D%25E3%2581%2586&gender=male

送信データが、URLの一部として送信されたわけです。送信データとURLは ? で区切られます。 そして、送信データの部分が $ENV{QUERY_STRING} に格納されます。 送信データは、各入力項目ごとに & で区切られます。 例では、ニックネームと性別にあたります。

ニックネームの値は「たろう」のはずなのに %25E3%2581%259F%25E3%2582%258D%25E3%2581%2586 となっています。 これはブラウザーがデータを送信する際に、URL エンコードをしたからです。

GET メソッドで送信されたデータを CGI で受け取るには、$ENV{QUERY_STRING} の値を、上で説明したように分解する必要があります。 さらに、URL エンコードされた値をデコードする必要があります。

POST メソッドのデータの受け取り方

POST メソッドで送信されるデータは、GET メソッドのように URL の一部として送信されるわけではありません。 したがって、CGI では $ENV{QUERY_STRING} からデータを受け取ることができません。 POST メソッドで送信された場合、$ENV{QUERY_STRING} にはデータは何も入ってきません。

その代わり、CGI はサーバーの STDIN (標準入力) 経由で受け取ることができます。 Perl スクリプト上では、以下の記述で取得することができます。

read( STDIN, my $data, $ENV{CONTENT_LENGTH} );

上の例では、スカラー変数 $data に送信データが格納されます。 $ENV{CONTENT_LENGTH} は、ブラウザーからのリクエストヘッダーにある Content 長 (バイト)です。 次の例をご覧ください。

<form action="https://www.futomi.com/lecture/form/exec/get-post-test.cgi" method="post">
  <p>ニックネーム:<input type="text" name="nickname" value="たろう"></p>
  <p>性別:
    <input type="radio" name="gender" value="male" checked> 男性
    <input type="radio" name="gender" value="female"> 女性
  </p>
  <input type="submit" value="送信">
</form>

ニックネーム:

性別: 男性 女性

この例は、前述の GET メソッドの説明に使った例とほぼ同じですが、form タグの method 属性の値が post に書き換えられています。

スカラー変数 $data には次の値が格納されます。

nickname=%E3%81%9F%E3%82%8D%E3%81%86&gender=male

これは、GET メソッドの時の $ENV{QUERY_STRING} に格納されるデータと同じです。以降の文字列の分解については GET メソッドの場合と同様です。

GET と POST メソッドを区別する方法

GET と POST で、データの受け取り方に違いがあることはご説明した通りですが、 次のような場合にはどうすれば良いのでしょうか。

これを実現するためには CGI 側でリクエストが GET なのか POST なのかを区別しなければいけませんが、 Perl スクリプト上では $ENV{REQUEST_METHOD}の値を見ることでメソッドを区別することができます。 この値は、以下の通りです。

メソッド $ENV{REQUEST_METHOD} の値
GET GET
POST POST

この値を見て、Perl スクリプト上で場合分けをすればいいのです。 前述の GET と POST のサンプルは、いずれも同じ CGI が担当していますが、 ソースコードは次の通りです。

use strict;
use warnings;

print "Content-type: text/plain; charset=utf-8\n";
print "\n";

if ( $ENV{REQUEST_METHOD} eq "GET" ) {
    print $ENV{QUERY_STRING};
}
elsif ( $ENV{REQUEST_METHOD} eq "POST" ) {
    read( STDIN, my $data, $ENV{CONTENT_LENGTH} );
    print $data;
}

まとめ

GET と POST のデータの受け取り方を説明しましたが、各パラメータの分解や URL デコードなど、 意外に面倒と感じたのではないでしょうか。 フォームデータの扱いについては、すでに優秀な Perl モジュール や ライブラリー があります。 これらを使ってデータを取得することをお勧めします。

Perl モジュールとしては、CGI.pm が最も有名でしょう。 かつては Perl5 に標準モジュールとして組み込まれていた定番モジュールです。 最新の Perl5 では CGI.pm は標準モジュールから外されていますが、いまなお、 多くのレンタルサーバーで利用することができます。

CGI.pm の使い方については「CGI.pm を使ってみよう」で詳しく解説していますので、 ぜひご覧ください。