プロが教えるわが家の防犯対策術!

先日「cgiからcgiを呼び出す方法」で質問させていただいたのですが、
その後の状況にて新たな質問をさせていただきます。

【経緯】
もともと単体で動作していたcgi Aに対して新たにcgi Bを作成し、
cgi Bからcgi Aを呼び出したいのですが、うまくいきません。
 cgi A:アンケート回答フォームのようなもの
 cgi B:認証フォーム(コードを入力してDB内のデータと一致したらcgi Aを呼び出したい)

【その後の状況】
exec("perl A.cgi");
とすることで cgi A の画面を表示することが出来ました。

【新たな質問】
(1) cgi A の画面は表示されましたが title は cgi B のままです。
 exec で cgi A が呼び出された時点で title は cgi A にならないのでしょうか?
(2)いろいろなサイトに、呼び出し先に
 "Content-Type: text/html; charset=Shift_JIS\n\n"
 の記述があってはダメとの説明がありましたが、この記述がないと
 cgi Aにてデータを入力し、送信ボタンを押すと
 500 Internal Server Error(malformed header from script. Bad header~)
 が表示されてしまいます。
 Content-Type の記述があれば、cgi A の送信ボタンを押すことによって
 入力したデータがDBに格納され、また、title も cgi A のタイトルが表示されます。

【cgi A が呼び出された時点の画面イメージ】
 cgi B のタイトル
 Content-Type: text/html; charset=Shift_JIS
 cgi A のタイトル
  cgi A の入力項目
  cgi A の送信ボタン

cgi A の送信ボタン(submit)を押すことによって、完全に cgi Aに制御が渡るということでしょうか?
cgi A を呼び出した時点で完全に cgi A に制御を渡したいのですが、
どなたかアドバイスをお願いします。
cgi A の構成を変える必要があるようであれば、その当たりのアドバイスもお願いします。

A 回答 (5件)

横から失礼します。



一度認証して、LocationでちがうCGIを呼び出すのは 認証した意味がなくなると思うのですが・・
というのも、認証しないでいきなりCGI-Aを呼び出したらどうなります?
認証するということは、一度の処理ですべてを行わないと意味がなくなります。

なので、AとかBとかに分けないで1つにするのが初心者向けですよー
もうちょっと理解が深まれば、CGI-Aをrequireして1つのCGIとしてあつかえることができます。

さらに簡単にするのなら、1つのCGI内でサブルーチンにして分けて処理を行う方向性が後々の変更のため
そして、コード量を減らすことや なによりエラーをなくすためにはいい方法です。
例)
認証サブルーチン、DB問い合わせサブルーチン、アンケートフォームサブルーチン、アンケート受け取りサブルーチンetc


そしてこれらを1つのCGIで処理するページングを行いたいのであれば、引数としてURLに含めるのが一般的です。
例)
xxx/B.cgi?que=form (認証して入力用のアンケートフォームを表示するなど)
xxx/B.cgi?que=input (認証してアンケートフォーム入力値を受け取り結果を表示するなど)

上記のように常に認証しないと、意味がなくなります。


それと、system関数やexec関数は同じプログラム言語を使うならお勧めできません。
環境によっては使えないことも多々ありますし、なによりコスト(サーバ負荷)とリスク(トンネル)が高いです。
この辺はもう少し言語に対する理解が深まってから手を出すべきです。(初心者に勧めるべきことではありませんよね)

横槍のようで何なんですけど、なんだか変な方向に行きそうなので、口を出してしまいました。
    • good
    • 0
この回答へのお礼

ご指摘ありがとうございます。

>認証しないでいきなりCGI-Aを呼び出したらどうなります?
この点はいろいろなサンプルを参考に、HTTP_REFERERを意識しておりました。

>なので、AとかBとかに分けないで1つにするのが
>初心者向けですよー
>もうちょっと理解が深まれば、CGI-Aをrequireして
>1つのCGIとしてあつかえることができます。
requireも頭の片隅には残してあります。
ご指摘の通り、他にもアドレス登録フォームやメール送信フォームなどが存在しており、全体を通してDBエラー処理など共通ロジックが個々のプログラムに存在している点は無駄だなぁと感じておりました。

まずは1つのプログラムが動くようになってから全体を通して最適化して行こうかなと。

今回もAとBは1本化します。
ありがとうございました。

お礼日時:2011/01/31 09:35

perl歴9日でここまでやれるとは驚きです。


わたくしなどこの辺まで来るのにもっとかかったように思います。
皆さんが回答されたことを簡単にまとめてみます。

単純なサンプルコードとしては
--------------------------------------------------
## 認証コードの入力
my $sPWC = '認証コード';
my $sDBC = 'DBのコード';
## 入力されたコードとDB内のコードの突き合わせ
if($sPWC eq $sDBC){
 ## 一致したらAを呼び出す
 print "Location: http://www.hoge.jp/CGI_A.cgi\n\n";
}else{
 print "Content-type: text/plain\n\n";
 print "認証エラー\n";
}
--------------------------------------------------
となります。
HTTPヘッダはブラウザを制御する命令です。
「Content-type」とくればこのあとデータが何らかの形式できますぜという命令ですし
「Location」とくればブラウザのアドレスにこの後のデータを入れてアクセスしますぜという命令です
改行が2回連続で来ればヘッダは終わりになります。

HTMLの出力はLocationで呼び出されたCGI_A.cgiが出力するように作ります。
もちろんそのときは「Content-type: text/html\n\n」をまず出力してそのあとHTMLデータを出力させるようにします。

> 今の僕の知識レベルではA内で全て処理した方が良い気がしてきました。
そうした方がより効率的なのが直感でわかってらっしゃるんです。
認証が一致したらCGI_Aの処理を丸々おこなえば良いだけなんでAとBに分ける必要性はほとんどありません。

蛇足ですが
 execやsystemなどで呼び出したプログラムはCGIプログラムの「子」になります。
 CGIそのものはWebサーバによって呼び出された「子」なのですが、上記プログラムは「孫」はWebサーバと直接つながりが無いのでCGIではなくなります。
 関連を表現するとこんな感じでしょう。
 [WEBサーバ]<=>(CGI)<=>[Perlプログラム]=>(exec)=>[Perlプログラム:結果待ち不要]
 [WEBサーバ]<=>(CGI)<=>[Perlプログラム]<=>(system)<=>[Perlプログラム:結果待ち必要]
 CGI(Common Gateway Interface)というのはインターフェースというぐらいですからプログラムを指すのではなくWebサーバとプログラムとの橋渡しする仕組みのことなのです。 ですからプログラムならCでもRubyでも標準入出力を持つ言語なら何でもいいのです。
 execとsystemの違いも注目してください。
 ご理解の助けになれば幸いです。
    • good
    • 0
この回答へのお礼

アドバイスありがとうございます。

もともとAが存在しており、後からBの機能を追加することになったので単純にBを新規で作成することにしました。
せっかく動いていたAに手を加えるのも何となく嫌だったので(^^;)

背中を押していただいたので、AとBを1本化します。

ありがとうございました。

お礼日時:2011/01/31 09:25

1番目のものです。



まずは認証などを引き継がないでいいんですね。
つまり、直接Aが呼ばれても問題ないということで。


この場合もっとも簡単なのは、B内で処理が通ったあとに
"Content-Type: text/html; charset=Shift_JIS\n\n" の代わりに
"Location: http://xxxxxx/A.cgi\n\n"
を出力するだけです。

事前に Content-type…の行を出力していたらダメですよ。
ブラウザ側から見て最初に渡される文字列が「Location: (A.cgiのURL)\n\n」になっている必要があります。
これで、ブラウザは「Aに飛ばないといけないんだな」ということがわかり、改めてAのほうを要求します。


ちょっと長いですが、参考URLのページが詳しいです。
ご参照ください。

参考URL:http://www.rfs.jp/sb/perl/03/03.html
    • good
    • 0
この回答へのお礼

ありがとうございます。
参考URLにてサーバ側とクライアント側のイメージが深まりました。

B内では
・認証コードの入力
・入力されたコードとDB内のコードの突き合わせ
・一致したらAを呼び出す
ということをやりたいのですが、HTMLを出力しないでも
実現できるものなのでしょうか?

今の僕の知識レベルではA内で全て処理した方が良い気がしてきました。

お礼日時:2011/01/28 09:25

一応、前の回答に


> あと、httpのヘッダが重複しないように注意。
と書いてあったんだけど.....


CGIの基本として「サーバーでどんなことをしていようが、ブラウザが処理するのは、送られてきた成果物のみ」というのがあります。

CGIの出力は

httpのヘッダ(複数行ある場合も)
改行のみの行
データ本体(ヘッダが content-type: text/htmlだったら htmlのデータ)

という形式になっています。この形式でさえあれば、サーバー側で複数のプログラムで処理されていようが、1つのものとしてブラウザは処理します。

> 【cgi A が呼び出された時点の画面イメージ】
のようにブラウザで表示されるのは、そのように送られてきたからです。

逆に、
> Content-Type: text/html; charset=Shift_JIS
> cgi A のタイトル
...
だけが送られてきたら、URLがB.cgiだろうがC.cgiだろうが、cgi Aだけを指定したときと同じ表示になります。

対処法は
「exec までの間に cgi Bからは何も出力しない」
「初回や認証失敗等で単独でcgi Bを表示する場合だけ、httpヘッダも含めてcgi Bから出力する」
という様にcgi Bの処理の順番を入れ替えることです。

現在は
cgi Bが出力したhttpヘッダ→httpのヘッダとして認識
cgi Bが出力したhtml(の一部?)→htmlのデータの一部として表示
cgi Aが出力したhttpヘッダ→「htmlのデータの一部として表示」
cgi Aが出力したhtml→htmlのデータの一部として表示
となっているようです。


> 呼び出し先に
> "Content-Type: text/html; charset=Shift_JIS\n\n"
> の記述があってはダメとの説明がありました

これは、「ヘッダは常に呼び出し元で出力される」場合で、呼び出し先は「常に呼び出し先としてしか使用されない」場合です。
cgi Aは単独で使用されることがあるので、この出力は必要です。
    • good
    • 0
この回答へのお礼

分かり易い説明をありがとうございます。

>一応、前の回答に
>> あと、httpのヘッダが重複しないように注意。
>と書いてあったんだけど.....
すみません。httpヘッダの理解が乏しいまま、
execが通った喜びで先に進んでしまいました。

>「exec までの間に cgi Bからは何も出力しない」
>「初回や認証失敗等で単独でcgi Bを表示する場合だけ、
>httpヘッダも含めてcgi Bから出力する」
今回、事前に社員宛にメールで送信してある認証コードを cgi B から入力させ、DBに登録済のコードと一致したら cgi A を表示してアンケートを入力してもらう、
ということを実現したいのです。

今の僕の知識レベルでは、認証コードを入力するためのHTMLを出力する方法しか見いだせていないのですが、今のcgi AとB を1本に纏めてしまった方が簡単でしょうか?

引き続きご指南を宜しくお願いします。

お礼日時:2011/01/28 09:55

これ、変更すべきはBのほうです。


Bで何らかの処理(ログイン処理など)を行っていると思いますが、それが成功した時に「『Aを読め』とブラウザに伝える」ことが必要になります。


ブラウザ側とサーバ側に分けて考えると理解しやすいです。

まずブラウザはBを呼び出しますよね。
Bの中でAをexecしたとしても、それはサーバ側の内部的な話。
ブラウザとしてはBを呼んでいるという認識ですから、そもそもAが呼ばれているかどうかなんてわかりません。

というわけで、Aから出力されたHTMLをブラウザに返していても、ブラウザから見れば「BというページのHTMLが送られてきた」としか認識できないんです。

なので、Bで処理が通った時点で、ブラウザに対して明示的に「Aを呼びなさい」という指示を与えてあげる必要があります。
やり方としてはいくつもありますが、おそらくBで取得した認証情報などをもう一度Aに引き継ぐ必要があると思いますので、BでセッションのCookieを食わせてAでチェックするとか、隠しフォームにAで使う値をセットしてJavaScriptでPOSTするなどのやりかたが必要になりますね。
(このあたりは実装によって変わるので、ちゃんとアドバイスできませんが)

いずれにしても、単純に内部でBからAを呼び出しただけではブラウザは感知できないので、再度ブラウザからAそのものを呼び出さなければならない、ということです。
ご参考になれば。
    • good
    • 0
この回答へのお礼

ものすごく分かり易い説明に感激です。
ありがとうございます。

宜しければ更にご教示いただけませんか。
認証情報をAに引き継ぐ必要は無いので一番簡単な方法でお願いします。
今回の仕組みは社内use onlyで、会社としてオープン系初tryなので、シンプルイズベスト!です。
かくいう私もperl歴9日目です(^^;)

宜しくお願いします。

お礼日時:2011/01/27 14:06

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!