CGI入門

HTML文書内にあるデータ入力窓とそれらを受け渡すスクリプト 名を書いておき、ボタンを押すとそのスクリプトに入力値が 渡るようなしくみをCGI(Common Gateway Interface)という。

CGIのしくみ

CGIのデータ入力窓の集合を入力フォームという。 以下の入力フォームに値を入力、あるいは選択し「OK」ボタンを押してみよう。

名前↓
さんの誕生日は?

上の入力窓に入れられた値は全て入力名という特別な 変数でCGIスクリプトに送られる。 自分で作るプログラムではその変数を受け取りそれに応じた処理をした上で、 結果を表すHTML文書を出力する。

[taro]さんの誕生日は?
[1992]年[ 3]月[1]日
[OK][reset]

「Rubyスクリプト」
値を受け取り
HTMLを出力
<html>
<head>
<title>結果発表!</title>
</head>
</body>
<p>........</p>
</body>
</html>

CGIの作り方

CGIは、利用者にデータを入力してもらうためのHTML文書と そこから送られて来るデータを受け取って処理をするプログラムファイルを 作る。さらに、Webサーバに対して「ここでCGIプログラムを利用する」ことの 宣言を行なう必要がある。

  1. CGI利用宣言 → .htaccessファイルの作成
  2. 入力フォームのHTML文書作成
  3. 処理プログラムの作成

順を追って行なう。なお、 慣れたあとは2と3をまとめて1つの Ruby プログラムで行うほうが楽であるが、 HTMLとCGI実行プログラムは分けたほうが理解しやすいので 以下の説明は分ける方法で記述する。

CGIプログラムの利用宣言

「この場所でCGIを利用する」ためのディレクトリを作成し、そこに .htaccessという名前のファイルを作成する。ここではたとえば、 ~/public_html/mycgi というディレクトリを作成し、 そこでCGI利用宣言をするものとする。まず

cd ~/public_html
mkdir mycgi

としてディレクトリを作成する。次にEmacsで ~/public_html/mycgi/.htaccess ファイルを新規に開き、 以下の内容を記述して保存する(コピーペーストしてよい)。

AddHandler cgi-script   .rb
Options +ExecCGI
AddType "text/html; charset=utf-8" .html

1行目と2行目により、このディレクトリに作成する .rb という名前で 終わるファイルはCGIスクリプトと認識される。3行目の記述により、 HTMLファイルの文字コードが UTF-8 であることを宣言する。 RubyプログラムをUTF-8で作成するので、HTMLファイルもそれに合わせる。

HTML文書の構成

次は入力フォームを構成するHTML文書を作る。入力フォームは form要素を使って記述する。form要素は

<form method="メソッド" action="./スクリプト">
 〜〜〜
</form>

のように記述する。メソッドの部分は GET または POST のどちらかを指定する。文章など、長いデータを入力させた いときはPOSTを指定する。

以下のform要素例は

入力させ、./btype.rb というスクリプトに 入力値を渡すためのHTML記述である(該当部分抜粋)。

<form method="POST" action="./btype.rb">
<p>お名前: <input name="namae" type="text" maxlength="40"><br>
血液型: <select name="blood">
  <option>A</option>
  <option>AB</option>
  <option>B</option>
  <option>O</option>
 </select><br>
<input type="submit" value="OK">
<input type="reset" value="reset"><br></p>
</form>

上記の入力フォームのみを含むHTML文書の例を btype.html に示す(ソース)。

次節で、./btype.rb の作り方を示す。

CGIスクリプトの構成

CGIスクリプトとなるRubyプログラムでは、最低限以下の内容を記述して 作成する。

#!/usr/bin/env ruby
# coding: utf-8

require 'cgi'
c = CGI.new(:accept_charset => "UTF-8")
print "Content-type: text/html; charset=UTF-8\n\n"

変数cはどんな名前でもよい。このあとに、HTML文書となり得 る文字列を順次出力していく。

require は、プログラムに必要な 外部モジュールを現在のプログラムに取り込む記法で、

require 'cgi'

はCGIプログラムを組むのに便利なクラスが定義された cgi.rb を取り込む宣言である。これで以下のようにCGIクラスが 使えるようになる(CGI.newの部分)。

CGIプログラムに渡された入力値を取り出すには、CGI.new を受け取った変数のハッシュ値として取り出す。以下の例はform入力された パラメータ namaeblood を単にHTML文書形式で 出力するRubyプログラムである。

btype.rb

#!/usr/bin/env ruby
# coding: utf-8

require 'cgi'
c = CGI.new(:accept_charset => "UTF-8")
print "Content-type: text/html; charset=UTF-8\n\n"

name = c["namae"]  # フォームに記入されたnamaeの値を取り出す
bt   = c["blood"]  # フォームに記入されたbloodの値を取り出す

# 「<<EOF」とすると、次に見つかる「EOF」まですべてを文字列として処理する
# これを「ヒアドキュメント」という
print <<EOF
<!DOCTYPE html>
<html>
<head><title>Blood type</title></head>
<body>

<h1>#{name}さんの血液型</h1>

<p>#{name}さんは#{bt.upcase}型ですね!</p>
</body>
</html>
EOF
# ↑このEOFまでがすべて文字列として処理される。CGI等で便利。

プログラム中現れる <<ヒアドキュメント といい、その次の行から、 <<の直後に書かれた単語が見つかる行までをまとめて 1つの文字列として括る記号である。改行文字を含む長文文字列を 扱いたいときに便利な記法であり、文字列中にダブルクォートやシングルクォー トを自由に入れることもできるため、ダブルクォートを多用することの多い HTML 文の出力には有用である。

さらにこの文字列中で利用している #{...}という記法はその部分をRubyの式としてその値に 置き換える働きを持つ。たとえば、

print "1+2は#{1+2}です\n"

とすると、

1+2は3です

と出力される。HTML出力文に変数結果を入れたい場合に #{...} は有用である。

このCGIを動かす例が以下の入力フォームである。

お名前:
血液型:

処理の切り替え

少し進んで、入力した値により出力を切り替える例を作成してみよう。 入力されうる値をキーとして、対応する文字列をハッシュで登録しておき、 メッセージ出力部分をハッシュの値を引くことによって切り替える。

お名前:
血液型:

btype2.rb

#!/usr/bin/env ruby
# coding: utf-8

require 'cgi'
c = CGI.new(:accept_charset => "UTF-8")
print "Content-type: text/html; charset=UTF-8\n\n"

name = c["namae"]
bt   = c["blood"]

kekka = {			# A, B, AB, O に対応する値をHashで設定する
  "A"	=> "えーっすなあ",
  "AB"	=> "だぶる!",
  "B"	=> "びびっとね",
  "O"	=> "おーすげー",
}
kekka.default = "人間なの??"	# Hashのデフォルト値(キーがない場合の値)

bt.upcase!	# 念のため大文字に変えておく

# ここ↓の <<EOF の部分が次の行からEOF行手前までの文字列に置き換わる
printf(<<EOF, bt, kekka[bt])	# 8行下の%sに入る
<!DOCTYPE html>
<html>
<head><title>Blood type</title></head>
<body>

<h1>#{name}さんの判定結果</h1>

<p>%s型のあなたは%s</p>
</body>
</html>
EOF

CGIプログラムのデバッグ

プログラム作成作業を進めるに当たって、ここの手順を取らないと非常に苦労する。

簡単なRubyプログラムでも、CGIとして動かすには困難がともなう。 なぜなら、プログラムを起動して出てくるエラーメッセージなどが見えないから である。文法エラーなどもWebブラウザ経由では見ることができない。

Web上で動かすプログラムは以下の手順で段階的に作成・デバッグする。

  1. ターミナルで直接動かしてエラーをなくす
  2. ローカルPC(目の前で使っているPC)で動かして詳細デバッグ
  3. 本番サーバで動かして最終調整

(1)ターミナルでテスト

cgi.rb モジュールを利用したプログラムはCGI経由だけでなく、 ターミナル上でもデバッグできる。上記の btype.rb の場合は、HTMLからプログラムに渡す入力名として namae, blood を利用している。nameに「太郎」を、bloodに「A」を代入した状態で デバッグする場合以下のように起動する。

echo 'name=太郎&blood=A' | ./btype.rb

つまり、入力名の値を

'入力名1=1&入力名2=2&入力名3=3&...'

の形式でechoコマンドで出力し、実行プログラムに送り込めばよい。 まとめると、以下のようなプログラム起動で確認する。

echo '入力名1=1&入力名2=2&入力名3=3' | ./作成中のプログラム.rb

正しい(と思われる)HTML文が出力されたらローカルPCのWebサーバでテストする 2の手順に進む(手順3に先に進んで詰まったら次の2に戻るのでもよい)。

(2)ローカルWebサーバでのテスト

本来Webサーバは専用サーバで動かすものだが、Rubyには簡易Webサーバ 機能があり手軽に動かすことができる。

webserv.rb


保存したwebserv.rbを起動すると以下のようなメッセージが出る:

chmod +x webserv.rb
./webserv.rb
http://localhost:1234/ファイル名
で接続してください

「ファイル名」の部分を作成中のCGIファイルにしてブラウザで開く。 たとえばbtype.htmlを作成中の場合はブラウザで

http://localhost:1234/btype.html

を開く(1234は人によって違う)。もしエラーが出ると、webserv.rb を起動したターミナルにエラーメッセージが出るので自分の作ったプログラムに 出された部分を選んで確認し、修正する。

(3)正式WebサーバのURLで閲覧してテスト

以上のような起動方法で、正しくHTML文書が出力されたら正式Webサーバの URLで開く。

たとえば以下のようなURLである。

http://roy.e.koeki-u.ac.jp/~cユーザ名/mycgi/ファイル名

ブラウザ経由でアクセスしてエラーになる場合は、Webサーバ(roy)に ログインし /usr/local/apache2/logs/error_log ファイルの 最後の方をtail -f で参照する。

ssh roy      # ←ログインパスワードを入れる
tail -f /usr/local/apache2/logs/error_log
: ↑動きっぱなしになるので C-c で止める↑

としておくと、アクセスするのと同時にエラーが出力される。 デバッグが終わったら tail を C-c で止め、 最後に roy からログアウトするのを忘れないこと。

formで使える入力用要素

HTML文書の form 要素内で、 ユーザからの入力を促すための主な要素を実例とともに示す。

以下の説明脇にある入力フォームに実際の値を入れて [送信] ボタンを押すと、この例にある入力名がどのように渡るかの結果が示される。 実際に、いろいろな値を入れて試してみること。

input要素

なんらかの値を入力させる場合に、input要素に入力方法と入力名を指定する。 入力方法は type属性で、入力名は name 属性で指定する。以下に入力方法の種別と使用例を示す。 他の属性も付けた例を示すが、 最低限必要なのは name である。 type を省略すると text と見なされる。 また、例中にある label 要素は入力に対応するラベルを結び付けるものである。

以下の例では,「送信」ボタンを実際に押すことで入力名がどのように Ruby プログラムに渡るかが分かるようになっている。

text

1行だけの文字列入力。

<p><label>メッセージをどうぞ:
<input name="v_text" type="text" value="Hello" size="40">
</label></p>

入力名 v_text に初期値 "Hello" を入れて40字分の枠を作る。


初期値を省略すると空欄になる。

password

text と同様だが、入力文字をアスタリスクで隠す。

<p><label>パスワード:
<input name="v_password" type="password" size="20" maxlength="12">
</label></p>

入力名 v_password に最大文字数12字のパスワード入力を入れる。

ただし、パスワードの値がCGIプログラムに渡されるときは 暗号化などは一切されない ので、盗聴されても構わないものでしか使えない。

checkbox

チェックボックスを作る。ONのときの値とOFFのときの値を value 属性で指定する。

<p><label><input name="v_checkbox_1" type="checkbox"
 value="morning">朝御飯OK</label><br></p>
<p><input name="v_checkbox_2" type="checkbox"
 value="night">夕飯OK</p>

「朝御飯OK」がチェックされていたら入力名 v_checkbox_1"morning" に 「夕飯OK」がチェックされていたら入力名 v_checkbox_"night" を代入させる。label 要素はチェックボックスとラベル文字列をグループ化し、 ラベル文字列のクリックでチェックが入るようになる。 「朝御飯OK」はlabel要素で結びついているので文字列クリックでよいが 「夕飯OK」はボタンをクリックしないとチェックが入らない。

夕飯OK

チェックされないときは空文字列 "" が代入される。

radio

ラジオボタンを作成する。name属性で同じ入力名名 を持つものどうしがグループ化され、どれかひとつをチェックすると 他はクリアされる。

<p>
<label><input name="v_radio" type="radio" value="1">地球人</label>
<label><input name="v_radio" type="radio" value="2">宇宙人</label>
<label><input name="v_radio" type="radio" value="3">仙人</label></p>

たとえば「仙人」を選ぶと入力名 v_radio"3" が代入される。どれも選択されていない場合は 空文字列が代入される。

一度押してしまったラジオボタンをクリアさせるには次の reset ボタンを配置しておく。

submit, reset

入力値の送信またはリセットボタンを作成する。

<p><input type="submit" value="[送信]">
   <input type="reset" value="[リセット]"></p>

←これを押すと上記の入力値がすべてリセットされる。

hidden

隠れ変数を設定する。CGIプログラムに必ず渡したい 値を見えないところで指定する。

<input name="v_hidden" type="hidden" value="第3ステージ">

上記を書いてもWebページには何も表示されないが CGIプログラムには入力名 v_hidden に 「第3ステージ」という値が代入される。

select要素

選択肢を提示してそのうち1つまたは複数を選ばせるには selectとoptionを組み合わせる。

好きな季節
<select name="v_select">
 <option>春</option>
 <option value="Summer" selected>夏</option>
 <option value="Autumn">秋</option>
 <option value="Winter">冬</option>
</select>

好きな季節
(value指定のない「春」を選択すると「春」そのものが値となる)

このように、selected を付けておいたオプションが 選択初期値となる(上の場合「夏」)。 selected を付けなければ先頭項目が選択された状態で始まる。

selectmultiple 属性を付けると 複数選択が可能になる。

好きな曜日
<select multiple name="v_select_multi">
 <option value="Sunday" selected>日</option>
 <option value="Monday">月</option>
 <option value="Tuesday">火</option>
 <option value="Wednesday">水</option>
 <option value="Thursday">木</option>
 <option value="Friday">金</option>
 <option value="Saturday" selected>土</option>
</select>

好きな曜日

Firefoxで複数選択するには Ctrlクリック で行なう。

テキストエリア

長文を入力させるためには textarea を用いる。 入力エリアの行と桁数はそれぞれ rowscols 属性で指定する。

<textarea name="v_textarea" cols="60" rows="5">
これを消してメッセージをどうぞ
</textarea>

その他のフォーム

フォーム制御の詳細は HTMl Standard 4.10 フォーム が参考になる。

練習問題

次のRubyプログラムはinput要素に値を入力してOKを押すと、 次の画面に進む、を繰り返すものである。

cgiloop.rb (これと同時に cgiloop.css も保存する)

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'cgi'
c = CGI.new
input = c["name"]
myname = File.basename($0)
# $0はプログラムのフルパス名、File.basename($0)するとファイル名部分のみ取り出す

print <<~EOF
	Content-type: text/html; charset=utf-8

	<!DOCTYPE html>
        <head><title>CGIループ</title>
        <link rel="stylesheet" type="text/css" href="cgiloop.css">
	</head>
        <body>
        <h1>CGIループだよ</h1>
EOF

if !input.empty? then
  printf("<p class=\"shout\">%sさんの明日にいいことがありますように!</p>\n",
         input)
end
print <<~EOF
	<form action="#{myname}" method="POST">
         <p>お名前をどうぞ:</p>
         <p><input type="text" name="name">
         <input type="submit" value="OK">
         <input type="reset" value="Reset">
        </form>
        </body></html>
EOF

  1. このプログラムを ~/public_html/mycgi/ (またはcgi-scriptが有効になっているディレクトリ)に保存して chmod +x cgiloop.rb し、ブラウザでURLアクセスして 挙動を確かめよ。

  2. input 要素の type="text" で名前を入力させる部分を type="checkbox" に変えて何かを入力させるものを作成し挙動を確認せよ。

  3. 同様に type="radio" に変えたものを作成し挙動を確認せよ。

  4. select要素で複数の選択肢から1つを選ぶように変えたものを作成し挙動を確認せよ。

  5. select要素で複数の選択肢から複数を選べるように変えたものを作成し挙動を確認せよ。

  6. textarea要素を使って長文を入力させ、それをメッセージとして表示できるプログラムを作成し、挙動を確認せよ。