Command disabled: backlink

目次へ

WEB編 POST渡しのパラメータを受け取る

[TODO: 動物さん]

前回までで、WEBフォームを使って、CGIにPOST形式でパラメータを渡す方法までを見ました。ここでは、それを受け取る方法を確認します。

GET形式では、パラメータは「環境変数」というものを経由して届きました。

それにくらべ、POST形式では、パラメータは「標準入力」というものを経由して入ってきます。

標準入力というものを、実際にCGIスクリプトを組む上で深く理解している必要はないと思いますが、プログラムを作るときの結構基本的な概念ではありますから、ここで一応説明しておいたほうがいいですよね。今まで適当にお茶を濁していた「標準出力」というやつともセットで説明します。この概念だけを説明するためには、WEBアプリを学んでいるという意識はいったん忘れてもらったほうがいいです。

標準入力、標準出力

最初に言っておくと、毎度のことながら「標準入力/出力」という言葉自体にはたいした意味はありませんからね。何が標準なの? とか考えずに行きましょう。

さて、まず、ひとつひとつの実行中のプログラムには、マイク入力端子と、スピーカ出力端子があるんだ、とでもイメージしてみましょう。そして、二つのプログラムが動くとき、片方のスピーカ出力端子と、もう片方のマイク入力端子がケーブルでつながれていたらどうだろうとイメージしてみましょう。

pythonにおいて、print "hello" とか書いて、画面に hello と出力させるとき、このプログラムが行っていることをちょっと理屈臭く述べてみると、「標準出力に hello と出力する」という動作をしたともいえるのです。対話シェルからこれを実行しているとき、このpythonのプログラムの標準出力は、黒字に白い、このディスプレイ表示用のプログラムに接続されます。なので、print の結果をユーザーは目で確認することができるのです。

じゃあ、pythonプログラムの標準出力を、たとえば他のプログラムの標準入力に接続したら?

サンプルスクリプトでも実行してもらうと話が見えやすいでしょう。試しに、下のふたつのスクリプトを自分の実行環境上に作りましょう。CGIとして書く必要はないですよ。スクリプト名は、pipe_out.py と、pipe_in.py としましょう。

pipe_out.py
for i in xrange(100):
  print "hello"
pipe_in.py
import sys
c = 0
for i in sys.stdin:
  c = c + 1
 
print "processed %d lines" % c

で、二つのスクリプトの標準出力と標準入力を連結させながら実行します。下のように記述します。(カレントディレクトリ名の表示は、人によって違うかもしれません)

D:¥workpy> pipe_out.py | pipe_in.py
            ^^^^^^^^^^^^^^^^^^^^^^^^
(実行結果は、processed 100 lines とかそんな結果が一行出るだけでしょう)

縦棒記号「|」で連結しているのに注意してくださいね。この記号、打ち込めますか。普通の日本語キーボードなら、キーの並びの右上にありますよ。シフトキーを押しながら入力ですよ。

この縦棒記号が、プログラム同士を連結して、前者の標準出力を、後者の標準入力につなげて流し込みますよ、ということを意味します。この縦棒のことを、パイプと呼ぶこともあります。

python で標準入力から流れて入ってくるデータを使うには、あらかじめ sys モジュールをインポートしておいてから、sys.stdin という変数にアクセスすればよいです。この変数を、最初からファイルがopenされているかのように扱えばいいです。前者のプログラムが終了したら、こちら側ではファイルの中身が終わったかのように見えますので、サンプルのような簡単な書き方でオーケーです。

この「パイプによる連結」は、三つ以上のプログラムを連携させるときにも使えます。下のようなコマンドが実行されたとすると、

D:¥workpy> data_origin.py | process1.py | process2.py | process3.py | result.py
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

まずは data_origin.py というpythonスクリプトが実行されて、たぶんそこでは何かがprintされて、その結果は画面に表示されずに、連結された process1.py とかいう名前のpythonスクリプトの標準入力として渡されて、process1.py はその結果をつかって何らかの処理を行って中間結果のようなものをprintして、そしてその結果は画面に表示されずに、連結された process2.py とかいう名前のpythonスクリプトの標準入力として渡されて、…つかれた。途中省略。で、最後に result.py が何かをprintすると、それが画面上にはじめて表示されるんだ、と、そんなふうに読めます。

これが、プログラムにとっての、標準入力と標準出力の存在意義です。ひとつの大きな仕事を、別の小さなプログラム同士の連携として書けるようになるわけですね。

このついでなので、パイプ記号のかわりに「<」とか「>」とかの記号を使った場合にも変った動作をさせられることを紹介しておきましょう。

まず、「大なり」のほう。下のようなコマンドを発行すると、

D:¥workpy> some_script.py > data.txt
            ^^^^^^^^^^^^^^^^^^^^^^^^^

some_script.py というスクリプトの実行結果が、画面に表示されるかわりに、まるごと data.txt というファイルに格納されます。実行結果をエディタで開いてじっくり確認したりする用事に使えます。また、Windowsでは、UTF-8 エンコードされた文字列がうまくコンソール上で表示しにくいので、一旦こうやって外部のファイルに吐き出してからあとでエディタで開いて確認、という役にも立ちます。

で、次は「小なり」のほう。

D:¥workpy> some_script.py < data.txt
            ^^^^^^^^^^^^^^^^^^^^^^^^^

この書き方だと、今度は、data.txt というファイルの中身を、some_script.py というスクリプトの標準入力に流し込むという表現になります。今までの練習問題で、残業時間とかそんなデータを集計するようなスクリプトを書きましたが、標準入力として扱っても同じような用事がこなせそうだと分かりますね。

まあ、パイプ記号のついで、ということで頭の片隅にでも入れてくれるとよいです。「>」とか「<」とかって、なんか、データを流し込んでるぞ、という気分が伝わって、覚えやすくないですか。「|」はちょっと直観的な理解がしづらいですけど。

で、WEBアプリの話に戻る

さて、POSTで渡されるパラメータは、一般に、ちょっと量が多くなることがしばしばです。なので、CGIスクリプトは、この内容を標準入力から受け取るという決まりになっているのです。

CGIスクリプトの動作について改めて考え直してみると、これは、標準入力と標準出力を介した、ウェブサーバーとpythonスクリプトのコラボレーションと捉えることができるのだ、と思えませんか。WEBブラウザと実際のやりとりをしているのは、Apache とか AN HTTPD とかいう「ウェブサーバー」という種類のプログラムです。ウェブサーバーは、CGIスクリプトの実行が必要になると、(この場合は python を実行して)指定されたスクリプトを実行し、WEBフォームから投げ入れられたデータをその標準入力に繋げて流し込み、(または環境変数を変更してそれを知らせ)、そしてスクリプトの実行結果は、その標準出力を介してウェブサーバーが吸い上げ、それをWEBブラウザにレスポンスとして渡すんです。頭の中で、これらの登場人物の関連を明確にイメージできるか確認してみてくださいね。ウェブブラウザ、ウェブサーバー、CGIスクリプト、の三者の関係です。

さて、こんなところで、「POSTメソッドで与えられたCGIの実行パラメータは、標準入力から入ってくる」ということの説明をしました。

実際に、この原理をもとに、パラメータの受け取りをやってみましょう。

…と言いたいところですが、こいつもまた、pythonに標準配布されているモジュールを使って、あまり深く考えずに処理できてしまうのでした。実は、GETメソッドで与えられたパラメータを受け取るためのモジュールが、この機能もばっちり兼ね備えているのでした。

つまり、以前紹介した書き方、

>>> import cgi
>>> f = cgi.FieldStorage()
>>> f.getfirst('page', '')

とかいう書き方をそのまま再利用すれば、GETから環境変数経由で来た値だろうが、POSTから標準入力で来た値だろうが、ぜんぶまとめて使いやすいように処理してくれるのです。

なので、POSTメソッドだのGETメソッドだのということは深く考えず、とにかくHTMLでうまくフォームを作れるようになれればそれでよいです。値の受け取りは簡単なのですから。

次回、フォームとそれを処理するCGIをそれぞれ作るという練習問題をやってみましょうか。

 
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki