CGIの作成

《 BACK 》    《 INDEX 》    《 HOME 》   

■ 初めに

 
HTMLについての基礎知識HTML講座
Perlプログラミングの基礎知識Perlプログラミング講座
CGI についての基礎の基礎知識CGI作成前の基礎知識


下図の流れがスクリプトの作成から Webサーバへファイルをアップロードしスクリプトを実行するまでの大雑把な流れになります。 ※流れの説明は図の下をご覧ください。



■ 作成手順

  1. スクリプトの作成
    先ずここから始まりです、スクリプトを作成しない事には話しになりませんからね (^3^)/記述方の説明はここ移行になります。

  2. 端末でテスト
    作成したスクリプトを作成が終わったからといってすぐサーバへアップしてはいけません先ず端末で十分テストを行って下さい。テスト環境構築は WWWサーバの立ち上げ方を参照して下さい。

  3. デバッグ
    Aでエラーが無くなるまでこの作業は続きます(-_-)゜゜Zzz

  4. サーバへロード
    Aでエラーが無くなったらサーバへスクリプトをアップします市販のソフト、フリーのソフト等色々なFTPソフトがありますので、使い勝手の良いものでサーバへアップして下さい。
    ※環境によって少々注意点がありますのでこちらを参照して下さい

  5. 環境設定
    プロバイダ毎にスクリプトを動作させる為色々な設定がありますので設定して下さい。きちんと設定しないと当たり前ですが正しく動作しません。パーミッションの設定等も行います。サーバにスクリプトをアップしたが動かない!!と言う場合は先ずこの設定を確認して下さい。

  6. 走行確認
    正常に動作しているか確認して下さい( ̄▽ ̄)V


■ 環境の違いによる留意点

『 Win系 』  
  index.htm=Index.htm # 同一のファイルとして扱われる
 
『 Unix系 』  
  index.htm×Index.htm # 違うファイルとして扱われる

■ パーミッションとは

 プロバイダのサーバとしてよく使用されているUNIX系のOSは元来複数のユーザに使用される事を想定しています(マルチユーザ)。しかしそこであるユーザの作成した個人的なファイルを他のユーザに参照されたりすると言う問題が出て来ます、そのためにファイル、ディレクトリの一つ一つに属性を付けることになります。
属性はユーザ毎、使用レベルを設定することが可能で、この属性のことを[パーミッション(permission)]と呼びます。

 



■ Perlパスの設定

調べていただけたらスクリプトにPerlによって実行させると言うことを1行目に記述しなくてはいけません。

パスが/usr/local/bin/perlの場合 → #!/usr/local/bin/perl

※この記述が1行目に無いとエラーとなってしまいます、必ず記述しましょう


■ ヘッダ

  1. HTML   
    Content-type: text/html

  2. 無編集のテキスト   
    Content-type: text/plain

  3. GIF   
    Content-type: image/gif

  4. JPEG   
    Content-type: image/jpeg
      
    ※その他多数ありますが省略します。

print "Content-type: text/html\n\n";

上記によってブラウザ側は Webサーバから送られてくるのは HTML(上記の例)だと識別します。実際の Perlからの処理結果の出力はこのヘッダを出力した後に出力します。

ヘッダには Content-type 以外にも多数あり複数のヘッダを指定する事も出来ます。その時はヘッダを連続して記述し一番最後のヘッダの後に空行を出力します。

CGIを利用しての HelloWorld

《 index.pl 》


print "Content-type: text/html\n\n"; # ヘッダの出力

# 実際に表示させる内容
print "<HTML>\n" ;
print "<HEAD><TITLE>Page Title</TITLE></HEAD>\n";
print "<BODY>\n";

print "Hello World";

print "</BODY>\n";
print "</HTML>\n";



<< 直接要求 >>

http://サーバ名/サーバパス/index.pl

<< HTMLからリンク >>

<A HREF="http://サーバ名/サーバパス/index.pl"> </A>


■ 環境変数

例)
$ENV { PATH }    # 環境変数PATHを表わす。

CGIで使用する主な環境変数

CONTENT_LENGTH 標準入力に格納されたPOSTによる入力データのバイト数
GATEWAY_INTERFACE Webサーバが実行しているCGIのバージョン(大抵1.1)
OS Webサーバの使用しているOS
HTTP_ACCEPT Webブラウザが扱えるContent-typeの一覧
HTTP_COOKIE クッキー情報
HTTP_HOST ドメイン名
HTTP_USER_AGENT CGIページを要求したWebブラウザとOSを特定できる情報
PATH サーバのパス
QUERY_STRING GETによりWebクライアントから送られるデータ
REMOTE_ADDR 訪問者のIPアドレス
REMOTE_HOST 訪問者のホスト名(取得できない時はIPアドレス)
REQUEST_METHOD データの受け渡し方法 GET か POST
SCRIPT_NAME CGIスクリプト名(URL)
SERVER_ADMIN Webサーバの管理者に関する情報
SERVER_NAME CGIを実行しているマシンのホスト名(取得できない時はIPアドレス)
SERVER_PROTCOL Webサーバが実行しているHTTPのバージョン情報
SERVER_PORT Webサーバが使用しているTCP/IPのポート番号(大抵は80)
SERVER_SOFTWARE CGIを実行しているWebサーバ名

連想配列(ハッシュ)についての詳しい説明は 《 こちら 》 を参照下さい。


■ フォームデータの取得

  1. GET

    http://127.0.0.1/cgi-bin/sample.plといった URLの後ろに"?"をつけ CGIへ渡すデータを続けて記述します。前途したように"?"以降の文字列が Webサーバの環境変数 "QUERY_STRING"へ格納されます。
      
    http://127.0.0.1/cgi-bin/sample.pl?Hello
      
    とすると"Hello"の文字列が"QUERY_STRING"へ格納されます。
      
    <FORM ACTION="http://127.0.0.1/cgi-bin/samplepl" METHOD=GET>
      
    このように FORM タグの指定させることも可能です。但し "GET" には指定できる文字列に制限があり(サーバによって異なります)、長いデータをスクリプトに渡す場合は後述するPOSTを用います。又スクリプトにどういった文字列を渡しているかもブラウザのアドレスバーに表示されてしまいます。

  2. POST
      
    基本的に FORM からしか "POST" は出来ません。スクリプトへ渡せるデータ長は無制限で渡されたデータの長さが環境変数 "CONTENT_LENGTH" へ格納されます(バイト長)。データ自体は前途したように標準入力( "STDIN" )へ格納されます。
      
    HTMLで次のように指定します。
      
    <FORM ACTION="http://127.0.0.1/cgi-bin/samplepl" METHOD=POST>
      
    データ読込みの際は次のように渡されたバイと数だけ受け取るようにします。
      
    read ( STDIN , $std_post , $ENV{CONTENT_LENGTH} ) ;

■ データのエンコード、デコード

エンコード(符号化)

1.スペース  →  "+"  への変換

2.入力コントロールと値が関連付けされてWebサーバへデータが送られる。

(Postで送り出したテキストボックスを例にすると)

Name 入力値
Text1 Hello


Text1=Hello


3.2.が複数ある場合1.のデータを"&"で結合する。

Name 入力値 Name 入力値
Text1 Hello Text2 World


Text1=Hello&Text2=World


4.7ビット Ascii文字以外の特殊文字、日本語などはパーセント"%"に続く2桁の16進数に置き換える。

Name 入力値
Text1 あいうえお


Text1=%82%A0%82%A2%82%A4%82%A6%82%A8


"GET" を指定した場合は呼出し時に上の 2, 3 以外の手順をやっておく必要があります。行わないと、~ が \~に変換されてしまったり、日本語が化けてしまったりします。

デコード

a.1に対するエンコード

$value =~ s/+/ /g ;

説)"+"を空白に変換する正規表現

a.2に対する処理 )

( $name , $value ) = split ( '=' , $data ) ;

説)データとフォームの分割

a.3に対する処理 )

@query = split ( '&' , $data ) ;

説)&による分割

  
a.4に対する処理 )

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


クエリを復元するサンプル

if ($ENV{'REQUEST_METHOD'} eq "POST") {
    read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
} else {
    $buffer = $ENV{'QUERY_STRING'};
}

@query = split(/&/, $buffer);

foreach (@query) {

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

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

    $query{$name} = $value;

}
例えば http://www.site-cooler.com/cgi-bin/sample.cgi?mode=test のような呼び出しの場合上のサンプルによって $query{mode} と言う記述でtestを取得することが出来るようになります。

■ バイナリデータの取扱

UNIX 系の OSでなく Win系プラットホームでは、テキストまたは ASCIIファイルとバイナリファイルの間に違いがあります。このため Win系でグラフィックの入出力を実行する場合はファイル、標準入出力のハンドルを binmodeにより明示的にバイナリモードに変更する必要があります。



■ 排他処理について

Webシステムでは複数のアクセス要求があり更新系のファイルなど複数の利用者から同時に更新要求などがあった時データに矛盾が出る場合があります。 その為複数の更新要求でも矛盾を起こさないようにする仕組み(排他処理)が必要となります。

通常 UNIX系のプラットフォームでは flock, symlink 関数を用い排他処理を行いますが Win系では 前途の関数が使用出来ない為別の方法で下記の条件をクリアする必要があります。

1)他のプロセスからアクセスされない事  ( 排他処理 )
2)プロセスが強制終了した時  ( 以上終了時の対応 )

1の条件の為にロックファイルを作成しそれにより排他制御をする事も考えられますが OPENではファイルの存在チェックと作成を同一のプロセスで行えない為ファイル存在確認とロックファイル作成の間にプロセスの切替が起こる可能性があり上手く行かない可能性があります。

例)

if ( ! -f "Lockfile.txt" ) ; # ロックファイルの存在確認
open ( LF ">Lockfile.txt" ) ;  # ロックファイル作成
}  

この例では if と open の間にプロセスの切替が起こり他のプロセスでもロックファイルを存在しないと認識して作成してしまう可能性があります。その為に存在確認と変更を同時に行える rename,mkdirといった関数を用います。

 《 例 》

# ロック用サブルーチン

sub lock{

    # @
    my %lf=(lockdir => './lockdir/',
            timeout => 30,
            trytime => 10 );

    $lf{fname} = shift || 'lockfile' ;

    # A
    $lf{source} = $lf{lockdir} . $lf{fname};

    for ( my $i = 0; $i < $lf{trytime}; $i++ , sleep 1 ) {

        if( rename( $lf{source}, $lf{lockname} = $lf{source}.time )){
            return \%lf ;
        }

    }

     # B
    my @flist = glob($lf{lockdir}.'*' ) ;

    foreach ( @flist ) {

        if ( /^$lf{fname}(\d+)/ ){

            if ( time - $1 > $lf{timeout}
                    and
            rename ( $lf{lockdir} . $_ , $lf{lockname} = $lf{source} . Time ) ){

                return \%lf
                
            }

             last;

        }
    }

    undef;

}

# Cロック解除処理
sub unlock{

    rename ( $_[0]->{lockname}, $_[0]->{source} );

}

# サブルーチン使用例

$lf = lock( 'Lockfile' ) || die ;

    《ロック期間》

unlock( $lf ) ;


 《 解説 》

  1. ロックの情報設定( 異常終了処理の判別時間、処理待ちの時間、ロックディレクトリ等 ) ロックファイルは複数のロックに対応する為引数でも指定できるように記述してあります。

  2. rename関数は既にファイルが存在や元のファイルが無いと偽を返す。これを利用し元のファイルが戻されるまでループを使い処理を待ちます。変更ファイル名は後に異常ロックと識別できるよう"ファイル名" + "作成日付"のフォーマットの様にロック開始時間が分かる情報を付けてあげる必要があります。

  3. 異常ロックの識別処理、Aで指定回数分リトライしてロックできない時に現在のロックファイルロック日付を確認し、指定時間より古いロックファイルであれば異常ロックと判断し自らのプロセスによりロックを行います。

  4. lock の戻り値としてロック情報(ハッシュへのリファレンス)を取得し、ロックファイルをロック前のファイル名に戻します。(これにより別プロセスのロックが可能になります)


《 BACK 》    《 INDEX 》    《 HOME 》   

Copyright (C) 2000-2004 Knave
http://www.site-cooler.com/