トップへ > Web Program Lab.> No.5 データの渡し方
ウェブ・プログラムの研究所 Web Application Programming Lab.
一つ前にもどる 目次にもどる 次のページへ
●データの受け渡し方(データ入力の方法)

これまでは、時間によるメッセージの変化やカウンターといった、わりとサーバー側で一方的に作成される内容のプログラムを見てきました。いわゆる出力というものです。

しかし、いろんなプログラムを作っていくときには利用者側でいろいろとコントロールしたいことも多くなってくると思います。掲示板に文字を書き込むこともそうですし、プルダウンメニューとかで指定した条件で検索をしたりです。

このように、利用者側からサーバー(プログラム)に対して何か入力をさせるためには、どのようにしたらいいのでしょうか?

主な方法としては、二通りあります。

ひとつは「GET」という方法です。
このGETという方法は、ブラウザから普通にHTMLの文書を要求するときと同じ方法になります。同じ?とは、普通URLいわゆるアドレスで「http://ww......」と呼び出しますよね?この方法です。

入力したいプログラムが「test.cgi」だとすると、その後ろに「test.cgi?happy」とすることで、プログラム自体に「」以降の文字列を送り込むことができます。

あちこちの掲示板やサーチエンジンのリンクボタンのURLを注意してみてください。「http://www.xxxxxx.com/bbs.cgi?mode=next&num=12....」というのを見たことはないですか?これがまさにそうなんです。

すごく単純ですよね。これがWebサーバーの中では、QueryStrings という環境変数に格納されて、プログラムの中で QueryStringsの中身を参照すると、「?」以下の文字を読み出すことができる、という仕組みになっているようです。

当然URL形式になるので、ここに日本語を書き込むと、文字化けしたようなデータに変換されて送信されます。サーチエンジンで検索したすぐあとのアドレスバーに「%82%A0%82%A2%82%DC%82%A2...」とかって表示されていて、「なんだろう?」とか思ったことはないですか。あれが、変換された日本語などの表示なんです。

もうひとつ方法があります。
「POST」と呼ばれる方法です。一番最初の回でブラウザはHTMLソース以外にヘッダーという情報を受け取っているという話をしたことを覚えているでしょうか?このような情報は受け取るときだけではなく、送信するときも同じように見えない情報としてサーバーに送信されています。この見えない場所にデータを埋め込んで送信する方法です。

見えないのに、どうやって送信するかということですが、方法はすごく見慣れた方法で行います。

いわゆるフォームというのを使ってします。「<form action="" method="">」というタグですね。ここの「method」のところに「method="post"」と送信方法を指定することで、POSTとしてフォームで入力された内容がサーバーに向けて送信されているのです。


<form action="http://....." method="post">
 <input type="">......

 ここのインプットタグの内容がプログラムに入力される

 </form>


じゃあ、「method=""」のところに「GET」って入れたらどうなるの?って思いませんか。 ご推測のとおり、フォームの情報がURLになって送信されるのです。大手のサーチエンジンなどは、ほとんど「検索」ボタンのフォーム部分が「method="get"」となっているので、検索したあとは、アドレスのところに変な文字が表示されるんですよね。

ただ、「POST」と「GET」、どちらの方法を使うべきかは、その目的にもよりますが、一般的に「POST」を使ったほうが安全性が高くなるといわれています。その理由は、「GET」で送られた内容はURLとして送信されるので、目に付きやすいし経由するサーバーのログに残る、「POST」は普通なら見えないような形で送信されるしログとしても残らないということらしいです。

また、「GET」だと、ブラウザ(クライアント)側に履歴として残ったりすることもありますし、URLでプログラムに引数を渡すことが可能なので、いたずらされやすいという点もあると思います。 ただ、「GET」にも別の利点があり(これは後述します。)、どちらを使うかは場合に応じてということですね。


●プログラムではどのように入力を実装するか?

では、実際にどのようにデータを取り込むかを考えてみたいと思います。

さきほどプログラムの「?」以下が送信されると書きましたが、実際フォームなどでどのようなデータが送信されているかをみてみるために、下のような簡単なプログラムを作って見ました。 これで、フォームの各項目を入力して「Submit」ボタンを押したときに、どのようにデータが送信されているかがわかります。

POSTの場合 GETの場合
1つ目(name1):
2つ目(name2):
3つ目(name3):
1つ目(name1):
2つ目(name2):
3つ目(name3):

よく見ると、「name1=abc&name2=cde&......」となっています。 フォームの名前である項目名とその値がイコールでペアになって送信されているのがわかりますでしょうか?しかもそれぞれが&でつながっています。


 項目名1=値1項目名2=値2項目名3=値3


よって、これらのデータを処理するためにはそれぞれの値をばらばらに切り直して、プログラムの中で使いやすいように加工する必要があります。

全ての項目および値のペアは「」で区切られていますので、まずこれをそれぞれのペアごとに分解する必要があります。

また、バラバラにしたペアを変数として扱いやすいようにするために、それぞれの項目名を引数とするハッシュに取り込みます。(Perlでの常套手段です。)

では、実際に外部からデータを入力(取り込む)するためのプログラムの解説をしたいと思います。


●入力するためのプログラムソース解説

下のソースは、「項目名1=値1&項目名2=値2&項目名3=値3」といったデータを$bufferという変数に取り込むための処理です。

この場合、先ほども書きましたが、POSTで送信された場合とGETで送信された場合で処理が違う(格納される変数が違う)ため、場合分けを行っています。


01: if ($ENV{'REQUEST_METHOD'} eq "POST") {
02:   read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
03: } else {
04:   $buffer = $ENV{'QUERY_STRING'};
05: }


【簡易解説】
01: もしも $ENV{'REQUEST_METHOD'} が POST なら
02:   STDINという変数から $ENV{'CONTENT_LENGTH'}の長さだけデータを読み出して
    $bufferに入れる
03: それ以外の場合(GET)は下の処理
04:   $ENV{'QUERY_STRING'}からデータを読み出して$bufferに入れる
05: 以上

まず、「$ENV{'REQUEST_METHOD'}」という特別な変数を参照して、要求が「POST」か「GET」かのチェックを行います。この「$ENV{'REQUEST_METHOD'}」は環境変数と言われ、要求等に関する様々な情報を知ることができます。これ以外にも様々なものがありますので、順次紹介したいと思います。

■ POSTで送信された場合:
POST の場合は、標準入力(STDIN)として入力されるので、上記のように $buffer に対して、$ENV{CONTENT_LENGTH} の長さのデータを入力する、というようにして処理します。(上記2行目。$ENV{CONTENT_LENGTH}は入力されたデータの長さをチェックする環境変数です。)

■ GETで送信された場合:
GETの場合は、URLとして送信されるので、そのデータの長さに限界があります。 また、送信されたデータは標準入力ではなく、$ENV{QUERY_STRING} に格納されるので、その中のデータをそのまま $buffer に入力しています。(上記4行目)

ちなみに、ここでは$bufferという名前の変数を使いましたが、どんな名前のものでもOKです。これで、プログラム内にデータを取り込むことができました。ただ、この状態では


 $buffer = 「項目名1=値1&項目名2=値2&項目名3=値3」


というつながった一つのデータに過ぎませんので、これを扱えるようにバラバラにする必要があります。


 foreach (split(/&/, $buffer)) {
   処理(ここはあとで解説)
 }


上記の処理では、$bufferに格納されたデータを「&」を区切り文字として分離する処理です。これを、&で区切れることができなくなるまで繰り返します。「foreach」とはPerlで利用できるそのような繰返し処理です。

ですが、&で切り取られたデータはどこにいってしまったのでしょうか? これは、Perlの書き方の問題なのですが、Perlはソースの表記をかなり省略できます。この場合、上記の太字の部分で切り取られたデータは、「$_」という汎用的な変数に格納されて、変数を使うことを明示しなくても使えるという非常に便利(あいまい)な機能です。この「$_」は明示して使うこともできますし、明示せずに省略して関数を使うことで、自動的に引数として渡すこともできます。

ですから、foreach(split(/&/,$buffer)) は正確に記述すると、


   foreach $_ (split(/&/,$buffer))


ということになります。
この切り取られたデータは

 $buffer=「項目名1=値1&項目名2=値2&項目名3=値3」

   ↓

 項目名1=値1
 項目名2=値2
 項目名3=値3


上記のように分解されますが、まだ項目と値が=でつながっているのでさらにこれらを分離する必要があります。
よって、「foreach(){}」の繰り返し処理の中で、この分解されたそれぞれを、さらに「」で分離して、


  ($keyword, $value) = split(/=/);

   ↓

  $keyword
  $value

*ちなみに、上の式は次の式の省略形です。
  ($keyword, $value) = split(/=/, $_);

というように変数にそれぞれ格納します。
これを、$keywordと$valueという関連づいた変数で扱うことができるように、ハッシュに格納します。(Perlでの常套手段です。)


   $FORM{$keyword} = $value;

     ↓

   $FORM{'項目名1'}=値1
   $FORM{'項目名2'}=値2
   $FORM{'項目名3'}=値3


これで入力されたデータをプログラム中で変数として扱うことができるようになります。 基本的にはこれでOKなのですが、ちょっと困る場合もあります。先ほど上でも書きましたが、「GET」でも「POST」でも送信するときに「%82%A0%82%A2%82%DC%82%A2...」という文字に変換されてしまいます。(エンコードという)

英数だけなら問題ないのですが、それ以外の文字や日本語の文字はこのように通常では読めない形式に変換されてしまうので、これを読めるように戻してやる必要があります。(デコードという)

そこで、$value に格納されたデータの中に、「%**」という形式で書かれたデータを見つけた場合、下記のように%だけと取り除いて、通常の16進数のコードを取り出す必要があるのです。(これは16進数だったのですね!)

そこで、次のような式が必要になります。


   $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;


これは、色々な処理を効率よく一行にしてしまっているので分かりにくいのですが、簡単に書くと下のような処理を順番にしています。


$value =~ s/(データ1)/(データ2)/eg;

これは、$value の文字列に「データ1」に該当する部分があれば「データ2」に置換するという処理です。

%([a-fA-F0-9][a-fA-F0-9])

この場合、「%」で始まるアルファベットまたは数字で構成される二桁の文字列(例、%82%A0%82%等)を見つけたら変換しなさい、 という命令となっています。

pack("C", hex($1)

変換の仕方ですが、%だけ抜き出して変換された16進数を10進数に変換して、 それを文字(Char)として更に変換したものに置き換えなさいという処理を行っています。

最後に、その内容を $value に上書きして終了。
つまり、「%82%A0%82%A2%82%DC%82%A2...」のような読めない文字が読める文字に変換されます。


入力を受付けるデータが英数文字だけならこれで問題ないのですが、日本語を使いたい場合もあります。このような場合は、文字コードに従った処理をする必要があるので、(WindowsはShift-JIS, UnixはEUCなど、、、)jcode.pl というモジュールにそのあたりの処理を任せます。


  # S-JISコード変換
  &jcode'convert(*value, "sjis", "", "z");


この部分は、説明が難しくなるので、日本語を使う場合には、これが必要だ、、、と覚えておいてもらえばOKだと思います。

あと、これは後ほどセキュリティのところで別途とりあげようと思うのですが、入力されたデータを処理に移す前に加工する必要があります。プログラム中に不特定多数からデータ入力を許すわけですから、そのデータをチェックする必要があるのです。データの中には、改行コード等が入っていることもあり(カット&ペーストで入力した場合など)、それが入力されると、処理がうまくいかないこともありえます。よって、ここでは、「\r」といった改行コードを無効にする処理をしています。また、空白は「+」とエンコードされてしまうので、これも通常のスペースとなるように戻す処理をしています。


   $value =~ s/\r//g;
   $value =~ s/\n//g;
   $value =~ tr/+/ /;


それ以外にも入力を避けたほうがいい文字はたくさんあります。これを「エスケープする」というのですが、その理由等についてはかなり奥が深く、ここで簡単に説明するには重要すぎるトピックなので、別途説明したいと思います。 (ちなみに「掲示板作成:データのサニタイズ」でも取り上げています。)

以上の処理をまとめたソースは次のようになります。


01: if ($ENV{'REQUEST_METHOD'} eq "POST") {
02:   read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
03: } else {
04:   $buffer = $ENV{'QUERY_STRING'};
05: }
06:
07:
08: foreach (split(/&/, $buffer)) {
09:   ($keyword, $value) = split(/=/);
10:   $value =~ tr/+/ /;
11:   $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
12:   
13:   # S-JISコード変換
14:   &jcode'convert(*value, "sjis", "", "z");
15:
16:   $value =~ s/\r//g;
17:   $value =~ s/\n//g;
18:
19:   $FORM{$keyword} = $value;
20: }


これで、入力されたデータに関する処理は以上なのですが、これだけ見ても何がどうなっているか理解できないかもしれませんので、次回で実際に簡単なプログラムを使って解説してみたいと思います。


(2004/1/29 by あいまい)
一つ前にもどる 目次にもどる 次のページへ

トップへ > Web Program Lab.> No.5 データの渡し方

copyright(C) 2001-2004 all rights reserved
『ウェブ・プログラムの実験場!』

by あいまいモード・コム(www.iMYmode.com)