目次

I. CGIで実現する投票ページの作成

Webページで提示したものに、閲覧者による投票によるランキングを作成してみよう。ランキングの作成の仕方は様々だが、順位の順番に並べるというタイプのwebページを作成しよう。

1.まずは、完成したときに表示されるwebページをHTML/CSSで作成する(外部設計)

ユーザ(閲覧者)にどのように見せたいかを、この段階でしっかり検討する。その検討のなかで、別の機能のアイディアも浮かぶかもしれない。教材として、「そば投票」というテーマにしてみた。ここのサンプルのようにHTMLファイルを作成してみた。このファイルranking01.htmlのソースコードは、次のようである。

[ranking01.html]
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="Shift-JIS">
<title>ランキング(サンプル)</title>
<meta name=viewport content="width=device-width, initial-scale=1">
<style type="text/css">
body{ 
  background-color: lightgray;
}
.vote{
  background-color: black;
  color: white;
  padding: 8px;
}
dt{
  font-size: xx-large;
  text-align: left;
  margin-top: 3em;
}
dd p{
  text-align: left;
}
form{
  display: inline;
}
h5{
  text-align: center;
  margin-top: 4em;
}
</style>
</head>
<body>
<div style="width: 80%; margin: 0 auto; text-align: center;">
  <h1>そば投票</h1>
  <p>もりかけ問題はさておき、寒い冬は、かけ蕎麦など温かい蕎麦が食べたくなります。
どのお蕎麦もおいしいけれども、売れないとお店のメニューからなくなってしまいます。
そうならないように、人気のないお蕎麦を積極的に注文する必要があります。
お蕎麦屋さんでつい選んでしまうお蕎麦に投票してください。
投票数の少ないお蕎麦が、メニューからなくなりそうなお蕎麦です。
それを意識して注文して、どのお蕎麦も楽しめる世の中にしましょう。
  </p>
  <dl>
    <!-- かけそば 始まり -->
    <dt>1位(325ポイント) かけそば</dt>
    <dd>
      <img src="./img/kakesoba.png" width="200" align="center" alt="かけそば" >
      <form method="POST" action="">
        <input type="submit" class="vote" value="コレをよく注文する!">
      </form>
      <p>シンプルなそばです。ついついネギもいれてしまいます。</p>
    </dd>
    <!-- かけそば 終わり -->

    <!-- てんぷらそば 始まり -->
    <dt>2位(320ポイント) てんぷらそば</dt>
    <dd>
      <img src="./img/tenpurasoba.png" width="200" align="center" alt="てんぷらそば" >
      <form method="POST" action="">
        <input type="submit" class="vote" value="コレをよく注文する!">
      </form>
      <p>てんぷらと言えば海老天。</p>
    </dd>
    <!-- てんぷらそば 終わり -->

    <!-- 月見そば 始まり -->
    <dt>2位(320ポイント) 月見そば</dt>
    <dd>
      <img src="./img/tukimisoba.png" width="200" align="center" alt="月見そば" >
      <form method="POST" action="">
        <input type="submit" class="vote" value="コレをよく注文する!">
      </form>
      <p>黄身は汁に混ぜないように食べるのが好きな人は多いはず。</p>
    </dd>
    <!-- 月見そば 終わり -->

    <!-- きつねそば 始まり -->
    <dt>4位(250ポイント) きつねそば</dt>
    <dd>
      <img src="./img/kitunesoba.png" width="200" align="center" alt="きつねそば" >
      <form method="POST" action="">
        <input type="submit" class="vote" value="コレをよく注文する!">
      </form>
      <p>油揚げの味も悪くない。</p>
    </dd>
    <!-- きつねそば 終わり -->

  </dl>

</div>

<h5><a href="about.html">このページについて</a></h5>

</body>
</html>

全体をdiv要素の内容として、中央揃えにするためにインラインのCSSで、幅を80%、mariginプロパティの左右をautoにし、text-alignプロパティはcenterにしている。

各々のそばは、定義リスト(dl要素)で並べている。順位、ポイント、名前は、dt要素に書いている。そばの画像と投票ボタンとコメントは、dd要素に書いているので、字下げされて表示される。 dl要素の項目となっている各々のそばがわかりやすいように、始まりと終わりをコメントで示した。例えば、かけそばは、<!-- かけそば 始まり --> から <!-- かけそば 終わり -->までである。

投票ボタンは、input要素でtype属性を"submit"で表示している。

POSTメソッドを使いたかったので、CGIスクリプトにパラメータとしてデータを渡すためにform要素を投票ボタンごとに定義している。

form要素はデフォルトではブロックレベル要素なので、CSSでdisplayプロパティの値をinlineにしてインライン要素にして、インライン要素のとなりに表示されるように工夫した。

2.順位付けする対象を区別する名前などを定め、送信フォームを作成する(内部設計)

[ranking02.html]
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="Shift-JIS">
<title>ランキング(サンプル)</title>
<meta name=viewport content="width=device-width, initial-scale=1">
<style type="text/css">
body{ 
  background-color: lightgray;
}
.vote{
  background-color: black;
  color: white;
  padding: 8px;
}
dt{
  font-size: xx-large;
  text-align: left;
  margin-top: 3em;
}
dd p{
  text-align: left;
}
form{
  display: inline;
}
h5{
  text-align: center;
  margin-top: 4em;
}
</style>
</head>
<body>
<div style="width: 80%; margin: 0 auto; text-align: center;">
  <h1>そば投票</h1>
  <p>もりかけ問題はさておき、寒い冬は、かけ蕎麦など温かい蕎麦が食べたくなります。
どのお蕎麦もおいしいけれども、売れないとお店のメニューからなくなってしまいます。
そうならないように、人気のないお蕎麦を積極的に注文する必要があります。
お蕎麦屋さんでつい選んでしまうお蕎麦に投票してください。
投票数の少ないお蕎麦が、メニューからなくなりそうなお蕎麦です。
それを意識して注文して、どのお蕎麦も楽しめる世の中にしましょう。
  </p>
  <dl>
    <!-- かけそば 始まり -->
    <dt>1位(325ポイント) かけそば</dt>
    <dd>
      <img src="./img/kakesoba.png" width="200" align="center" alt="かけそば" >
      <form method="POST" action="$ENV{SCRIPT_NAME}">
        <input type="hidden" name="soba" value="kake">
        <input type="submit" class="vote" value="コレをよく注文する!">
      </form>
      <p>シンプルなそばです。ついついネギもいれてしまいます。</p>
    </dd>
    <!-- かけそば 終わり -->

    <!-- てんぷらそば 始まり -->
    <dt>2位(320ポイント) てんぷらそば</dt>
    <dd>
      <img src="./img/tenpurasoba.png" width="200" align="center" alt="てんぷらそば" >
      <form method="POST" action="$ENV{SCRIPT_NAME}">
        <input type="hidden" name="soba" value="tenpura">
        <input type="submit" class="vote" value="コレをよく注文する!">
      </form>
      <p>てんぷらと言えば海老天。</p>
    </dd>
    <!-- てんぷらそば 終わり -->

    <!-- 月見そば 始まり -->
    <dt>2位(320ポイント) 月見そば</dt>
    <dd>
      <img src="./img/tukimisoba.png" width="200" align="center" alt="月見そば" >
      <form method="POST" action="$ENV{SCRIPT_NAME}">
        <input type="hidden" name="soba" value="tukimi">
        <input type="submit" class="vote" value="コレをよく注文する!">
      </form>
      <p>黄身は汁に混ぜないように食べるのが好きな人は多いはず。</p>
    </dd>
    <!-- 月見そば 終わり -->

    <!-- きつねそば 始まり -->
    <dt>4位(250ポイント) きつねそば</dt>
    <dd>
      <img src="./img/kitunesoba.png" width="200" align="center" alt="きつねそば" >
      <form method="POST" action="$ENV{SCRIPT_NAME}">
        <input type="hidden" name="soba" value="kitune">
        <input type="submit" class="vote" value="コレをよく注文する!">
      </form>
      <p>油揚げの味も悪くない。</p>
    </dd>
    <!-- きつねそば 終わり -->

  </dl>

</div>

<h5><a href="about.html">このページについて</a></h5>

</body>
</html>

各々のそばを区別するためのキーワードを以下のように定めた。これは、画像ファイルにするために"soba.png"を追加するだけで済むように工夫した結果である。

soba そばの名前 画像ファイル名
kake かけそば kakesoba.png
tenpura てんぷらそば tenpurasoba.png
tukimi 月見そば tukimisoba.png
kitune きつねそば kitunesoba.png

name属性の値を"soba"としたinput要素で、type属性を"hidden"として、value属性をそばのsobaを値としたものを追加した。このフォームからの送信するとCGIスクリプトへsaba=そばのキーワードというパラメータが送られる。

フォームのaction属性は、環境変数を使って、$ENV{SCRIPT_NAME}とした。Perlのスクリプト内では、$ENV{SCRIPT_NAME}はCGIスクリプトファイル名に置き換えられる。

3.送信フォームから送られたパラメータの値を処理するためのCGIスクリプトを作成する(内部設計)

3.1. テンプレート化されたHTMLファイル

Webページの表示は、外部設計で作成したranking02.htmlの複製から作成したranking03.htmlを使用する。

ranking03.htmlは、ranking02.htmlのそばの項目のdt、dd要素をすべて削除して、そこに<!--TABLE_LIST-->と書き加えるだけである。

[ranking03.html]
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="Shift-JIS">
<title>ランキング(サンプル)</title>
<meta name=viewport content="width=device-width, initial-scale=1">
<style type="text/css">
body{ 
  background-color: lightgray;
}
.vote{
  background-color: black;
  color: white;
  padding: 8px;
}
dt{
  font-size: xx-large;
  text-align: left;
  margin-top: 3em;
}
dd p{
  text-align: left;
}
form{
  display: inline;
}
h5{
  text-align: center;
  margin-top: 4em;
}
</style>
</head>
<body>
<div style="width: 80%; margin: 0 auto; text-align: center;">
  <h1>そば投票</h1>
  <p>もりかけ問題はさておき、寒い冬は、かけ蕎麦など温かい蕎麦が食べたくなります。
どのお蕎麦もおいしいけれども、売れないとお店のメニューからなくなってしまいます。
そうならないように、人気のないお蕎麦を積極的に注文する必要があります。
お蕎麦屋さんでつい選んでしまうお蕎麦に投票してください。
投票数の少ないお蕎麦が、メニューからなくなりそうなお蕎麦です。
それを意識して注文して、どのお蕎麦も楽しめる世の中にしましょう。
  </p>
  <dl>
    <!--TABLE_LIST-->
  </dl>

</div>

<h5><a href="about.html">このページについて</a></h5>

</body>
</html>

3.2. データを保持するファイル

投票結果や、webページの表示に必要なデータを保持するファイルを、point.datとする。point.datはタブ区切りファイルで、soba, ranking, point, name, image, commentの順番で各々のそばの情報を記録したものである。従って、初期状態として以下のように作成する。

[point.dat]
kake	1	0	かけそば	kakesoba.png	シンプルなそばです。ついついネギもいれてしまいます。
tenpura	1	0	てんぷらそば	tenpurasoba.png	てんぷらと言えば海老天。
tukimi	1	0	月見そば	tukimisoba.png	黄身は汁に混ぜないように食べるのが好きな人は多いはず。
kitune	1	0	きつねそば	kitunesoba.png	油揚げの味も悪くない。

point.datを作成するときは、ranking02.htmlをブラウザで表示して該当箇所(特にコメント)をコピー&ペーストすると、間違いが少なくなる。

3.3. CGIスクリプトファイル

送られたパラメータの値を処理と、パラメータがない場合にwebページを表示するという二つの役割を持たせたCGIスクリプトを、ranking.cgiという名前で下のように作成する。

[ranking.cgi]
#! /usr/bin/perl

# 2019-12-16. ランキング サンプル by SU.


# use strict;
### use Encode;
### use utf8;
### my $encoder = find_encoding('utf8');


my $top_page = "index.html";	# エラーの後に戻るページ
my $datafile = "point.dat";	# 投票データを保持するファイル
my $show_list_tmpl = "ranking03.html";	# テンプレート化された表示されるwebページ


my %param;
my @data;

%param = get_param();

if( $param{soba} ne "" ){
    read_records();
    update_data($param{soba});
}

read_records();
show_list();
exit;



################### subroutines ##################
sub update_data
{
	my $soba =$_[0];
	$soba =~ s/\s+//g;	# 空白は削除
	
	my $flag = 0;
	
	foreach (@data){
		if( $_->{soba} eq $soba ){
			$flag = 1;
			$_->{point} += 1;
		}
	}
	
	# $filenameに存在しない項目の場合は何もしない。
	if( $flag == 0 ){ return; }

	# 並べ替えてランキングを計算
	ranking();

	# 書き込みモードでファイルを開く
	open(OUT, ">$datafile") || &error("Cannot open $datafile: $!");

	foreach (@data){
		if( $_->{soba} eq "" ){ next; }
		print OUT "$_->{soba}\t$_->{ranking}\t$_->{point}\t$_->{name}\t$_->{image}\t$_->{comment}\n";
	}
	# ファイルを閉じる
	close(OUT);
}



# ポイント順に並べ替えて、順位を計算
sub ranking
{
	my( $rank,  $prev_point );
	
	# @dataをpointで降順で並べ替え
	@data = sort{$b->{point} <=> $a->{point}} @data;
	
	for(my $i = 0; $i < @data; $i++ ){
		if( $i == 0 ){
			$rank = 1;
			$data[$i]->{ranking} = $rank;
			$prev_point = $data[$i]->{point};
			next;
		}
		if( $data[$i]->{point} < $prev_point ){
			++$rank;
			$prev_point = $data[$i]->{point};
		}
		$data[$i]->{ranking} = $rank;
	}
}



sub read_records
{
	my( $soba, $ranking, $point, $name, $image, $comment);

	@data =();	# @dataを初期化

	# 読み取りモードでファイルを開く
	open(IN, "<$datafile") || &error("Cannot read $datafile: $!");

	while(<IN>){
		chomp;
		( $soba, $ranking, $point, $name, $image, $comment ) = split(/\t/, $_);
		# 無名ハッシュを配列にする
		push  @data, 
		{ 
			'soba'=>$soba,
			'ranking'=>$ranking,
			'point'=>$point,
			'name'=>$name,
			'image'=>$image,
			'comment'=>$comment
		};
	}
	# ファイルを閉じる
	close(IN);

}



sub show_list
{
	my $table_list;
	
	foreach (@data){
		my $item = <<"_ITEM_";

    <!-- $_->{name} 始まり -->
    <dt>$_->{ranking}位($_->{point}ポイント) $_->{name}</dt>
    <dd>
      <img src="./img/$_->{image}" width="200" align="center" alt="$_->{name}" >
      <form method="POST" action="$ENV{SCRIPT_NAME}">
        <input type="hidden" name="soba" value="$_->{soba}">
        <input type="submit" class="vote" value="コレをよく注文する!">
      </form>
      <p>$_->{comment}</p>
    </dd>
    <!-- $_->{name} 終わり -->

_ITEM_
		$table_list .=  $item;
	}

	# 読み取りモードでファイルを開く
	open(IN, "<$show_list_tmpl") || &error("Cannot read $show_list_tmpl: $!");

	print "Content-Type: text/html;charset=Shift_JIS;\n\n";
	
	while(<IN>){
		s/<!--TABLE_LIST-->/$table_list/;
		print $_;
	}
}



########### CGIの基本処理 #############
# 入力データの取得
sub get_param
{
	my($query, $key, $value, %param);

	if($ENV{REQUEST_METHOD} eq "GET"){
		$query = $ENV{QUERY_STRING};
	}else{
		read(STDIN, $query, $ENV{CONTENT_LENGTH});
	}


	foreach(split(/&/, $query)){
		($key, $value) = split(/=/, $_);
		$value =~ s/\+/ /g;
		$value =~ s/%([\da-f][\da-f])/pack("C", hex($1))/egi;
		### $value = $encoder->decode($value);
		$value =~ s/\r//g;
		$param{$key} = $value;
	}
	return %param;
}



# エラー表示
sub error
{
	print "Content-Type: text/html\n\n";
	print <<"---EOF---";
<html><head><title>Information</title></head>
<body>
<span sylte="color: red;"> $_[0]</span>
<form method="GET" action="$top_page">
<input type=SUBMIT value="最初のページに戻る">
</form>
</body></html>
---EOF---
	exit;
}

このCGIスクリプトのサブルーチン(ユーザ関数)は、以下のものである:

update_data()
更新された@dataの内容を、ranking()サブルーチンで並べ替えたあと、$datafileに書き出すサブルーチン。
ranking()
@dataをpoint順に並べ替えて、順位を計算するサブルーチンん。
read_records()
$datafileの内容を読み取って、@dataに格納するサブルーチン。
show_list()
$datafileの内容を$show_list_tmplに反映させてHTMLを生成して表示するサブルーチン。緑色でマークされている部分は、ranking02.htmlの同じ色でマークされている部分をコピー&ペーストして、個別のデータを$_->{キー}で置き換えて作成した。
get_param()
CGIで送られてきたパラメータとその値を解析して返すサブルーチン。
error()
die()関数の代わりに、プログラムをそこで終了させたいところで呼び出すと、情報を表示してプログラムを停止するサブルーチン。引数にエラーメッセージを入れれば、webページに表示される。デバッグのときに、どこまでうまく動いているかをチェックするのに役に立つサブルーチンである。

このCGIスクリプトのグローバル変数は、以下のものである:

$top_page
error()サブルーチンで表示されるページの、戻るボタンの戻り先。
$datafile
表示するそばの情報を保持するためのタブ区切りファイル。
$show_list_tmpl
テンプレート化されたWebページのファイル。<--TABLE_LIST-->が、順位の順に並べ替えらえた項目で置き換えられる仕組み。
%param
get_param()で解析されたパラメータと値の情報を保持するための連想配列。
@data
read_records()で$datafileから読み込んだ内容を保持する配列。$datafileをいちいち開かないで、@dataで情報を更新し、そのあとにupdate()関数で$datafileに反映させるという使い方をする。
$_
特殊変数

4.WWWサーバに設置して、動作をテストする

上で作成したranking.cgi、point.dat、ranking03.htmlとそれに必要な画像ファイルを、ドキュメント・ルートにrankingというサブディレクトリを作成して、そこにアップロードする。ranking.cgiのパーミッションは、755に設定する。

ブラウザでranking.cgiのURLに接続して、動作を確認する。

サンプルはこちら

不満足な点は、下位の項目に投票しても、ページの先頭が表示されてしまう点である。

II. バージョンアップ

不満足な点を解消するために工夫した。バージョンアップしたサンプルはこちら

[ranking01.cgi]
#! /usr/bin/perl

# 2019-12-16. ランキング サンプル by SU.
# 2019-12-16 v.1.1. 項目にアンカーを付けた。

# use strict;
### use Encode;
### use utf8;
### my $encoder = find_encoding('utf8');


my $top_page = "index.html";	# エラーの後に戻るページ
my $datafile = "point.dat";	# 投票データを保持するファイル
my $show_list_tmpl = "ranking03.html";	# テンプレート化された表示されるwebページ


my %param;
my @data;

%param = get_param();

read_records();

if( $param{soba} ne "" ){
    update_data($param{soba});
	read_records();
	print "Location:http://$ENV{SERVER_NAME}$ENV{SCRIPT_NAME}#$param{soba}\n\n"
}else{
	show_list();
}
exit;



################### subroutines ##################
sub update_data
{
	my $soba =$_[0];
	$soba =~ s/\s+//g;	# 空白は削除
	
	my $flag = 0;
	
	foreach (@data){
		if( $_->{soba} eq $soba ){
			$flag = 1;
			$_->{point} += 1;
		}
	}
	
	# $filenameに存在しない項目の場合は何もしない。
	if( $flag == 0 ){ return; }

	# 並べ替えてランキングを計算
	ranking();

	# 書き込みモードでファイルを開く
	open(OUT, ">$datafile") || &error("Cannot open $datafile: $!");

	foreach (@data){
		if( $_->{soba} eq "" ){ next; }
		print OUT "$_->{soba}\t$_->{ranking}\t$_->{point}\t$_->{name}\t$_->{image}\t$_->{comment}\n";
	}
	# ファイルを閉じる
	close(OUT);
}



# ポイント順に並べ替えて、順位を計算
sub ranking
{
	my( $rank,  $prev_point );
	
	# @dataをpointで降順で並べ替え
	@data = sort{$b->{point} <=> $a->{point}} @data;
	
	for(my $i = 0; $i < @data; $i++ ){
		if( $i == 0 ){
			$rank = 1;
			$data[$i]->{ranking} = $rank;
			$prev_point = $data[$i]->{point};
			next;
		}
		if( $data[$i]->{point} < $prev_point ){
			++$rank;
			$prev_point = $data[$i]->{point};
		}
		$data[$i]->{ranking} = $rank;
	}
}



sub read_records
{
	my( $soba, $ranking, $point, $name, $image, $comment);

	@data =();	# @dataを初期化

	# 読み取りモードでファイルを開く
	open(IN, "<$datafile") || &error("Cannot read $datafile: $!");

	while(<IN>){
		chomp;
		( $soba, $ranking, $point, $name, $image, $comment ) = split(/\t/, $_);
		# 無名ハッシュを配列にする
		push  @data, 
		{ 
			'soba'=>$soba,
			'ranking'=>$ranking,
			'point'=>$point,
			'name'=>$name,
			'image'=>$image,
			'comment'=>$comment
		};
	}
	# ファイルを閉じる
	close(IN);

}



sub show_list
{
	my $table_list;
	
	foreach (@data){
		my $item = <<"_ITEM_";

    <!-- $_->{name} 始まり -->
    <a name="$_->{soba}"></a>
    <dt>$_->{ranking}位($_->{point}ポイント) $_->{name}</dt>
    <dd>
      <img src="./img/$_->{image}" width="200" align="center" alt="$_->{name}" >
      <form method="POST" action="$ENV{SCRIPT_NAME}">
        <input type="hidden" name="soba" value="$_->{soba}">
        <input type="submit" class="vote" value="コレをよく注文する!">
      </form>
      <p>$_->{comment}</p>
    </dd>
    <!-- $_->{name} 終わり -->

_ITEM_
		$table_list .=  $item;
	}


	# 読み取りモードでファイルを開く
	open(IN, "<$show_list_tmpl") || &error("Cannot read $show_list_tmpl: $!");

	print "Content-Type: text/html;charset=Shift_JIS;\n\n";
	
	while(<IN>){
		s/<!--TABLE_LIST-->/$table_list/;
		print $_;
	}
}



########### CGIの基本処理 #############
# 入力データの取得
sub get_param
{
	my($query, $key, $value, %param);

	if($ENV{REQUEST_METHOD} eq "GET"){
		$query = $ENV{QUERY_STRING};
	}else{
		read(STDIN, $query, $ENV{CONTENT_LENGTH});
	}


	foreach(split(/&/, $query)){
		($key, $value) = split(/=/, $_);
		$value =~ s/\+/ /g;
		$value =~ s/%([\da-f][\da-f])/pack("C", hex($1))/egi;
		### $value = $encoder->decode($value);
		$value =~ s/\r//g;
		$param{$key} = $value;
	}
	return %param;
}



# エラー表示
sub error
{
	print "Content-Type: text/html\n\n";
	print <<"---EOF---";
<html><head><title>Information</title></head>
<body>
<span sylte="color: red;"> $_[0]</span>
<form method="GET" action="$top_page">
<input type=SUBMIT value="最初のページに戻る">
</form>
</body></html>
---EOF---
	exit;
}

III. 謝辞のページの作成

「このページについて」として、参考文献や謝辞など、このページのメタ情報を、about.htmlに作成しよう。

about.htmlの工夫としては、「戻る」のリンクはjavascriptのhistory.back()関数で元のページに戻ることができるようにしている。

[about.html]
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="Shift-JIS">
<title>Thanks</title>
<meta name=viewport content="width=device-width, initial-scale=1">
<style type="text/css">
body{ 
  background-color: lightgray;
}

h5{
  text-align: center;
  margin-top: 4em;
}
</style>
</head>
<body>
<div style="width: 80%; margin: 0 auto; text-align: center;">
  <h1>謝辞</h1>
  <p>このページを作成するのに、以下のサイトを参考にしたり利用したりしましたので、感謝します。
  </p>
  <ul>
    <li><a href="http://www.ipc.hokusei.ac.jp/~z00328/swdesign/ranking/rankingdoc.html" target="_blank">そば投票サンプルの説明</a></li>
    <li><a href="http://www.ipc.hokusei.ac.jp/~z00328/swdesign/ranking/ranking01.cgi" target="_blank">そば投票サンプル</a></li>
  </ul>
</div>

<h5><a href="javascript:history.back()">戻る</a></h5>

</body>
</html>

IV. フォルダの内容を隠すための工夫

URLで最後のファイル名を省略すると、index.htmlが省略されたものと解釈されてindex.htmlを表示しようとする。デフォルトの設定では、index.htmlが存在しない場合は、そのフォルダ内のファイルのリストをindex.htmlとして表示するようになっている。それは、あたかも舞台裏をさらけ出しているようで格好が悪いし、セキュリティー上も好ましくない。そこで、index.htmlをranking.cgiに自動的にジャンプするページにするという工夫をしてみよう。

[index.html]
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="Shift-JIS">
<title>そば投票(サンプル)</title>
<meta name=viewport content="width=device-width, initial-scale=1">
<meta http-equiv="Refresh" content="1;URL=ranking.cgi">
<style type="text/css">
body{ 
  background-color: lightgray;
}
</style>
</head>
<body>

<div style="width: 80%; margin: 0 auto; text-align: center;">
  <h1>そば投票</h1>
  <p>自動的にジャンプしない場合は、<a href="ranking.cgi">ここ</a>をクリックしてください。  </p>
</div>

</body>
</html>