サンプル・プログラム
パラメータの入力
44: <!-- 入力と表示 -->
45: <input type="text" id="from" size="4" value="" />から
46: <input type="text" id="to" size="4" value="" />の和は
47: <span id="sum"></span>です.
48: <input type="button" id="exec" value="計算" onClick="execute()" />
49:
計算をさせるためのボタン <input type="button" id="exec"> を用意した。このボタンをクリックすると、onClick属性で指定しているJavaScriptの execute()関数(後述)を呼び出す。
10: /**
11: * 計算と画面表示
12: */
13: function execute() {
14: //変数宣言
15: let a = document.getElementById('from').value; //開始値
16: a = parseInt(a, 10); //10進整数化
17: let b = document.getElementById('to').value; //終了値
18: b = parseInt(b, 10); //10進整数化
19: let n = 0; //合計値
20:
21: //aからbまでの整数の合計を求める
22: for (let i = a; i <= b; i++) {
23: n += i;
24: }
25:
26: //結果を表示する
27: document.getElementById('sum').innerHTML = n;
28: }
HTMLフォームから取り出した値は文字列なので、計算できるように parseInt関数を使って整数に変換する。
また、このブロック全体が execute()関数 として定義されている。関数については、追い追い解説する。ここでは、ボタンがクリックされたら呼び出されるプログラムと考えておいてほしい。
入力値のバリデーション
文字列が混じっていても parseInt 関数が 0 を返すので forループ が回らないが、小数の場合は計算してしまう。
そこで、小数や文字列が入力されたらエラーメッセージを表示して計算しないようにしてみよう。
まず、画面から入力されたデータ(文字列)を変数a, bに代入する。これは "input1.html" と同じである。
次に、入力されたa, bの先頭や末尾の空白を除去する。trim メソッドを使う。これはWebアプリに限らず、入力の前処理作法の1つだ。
それから、入力されたa, bをエスケープする。ここでいうエスケープとは、HTMLの特殊文字 &, ", ', <, > をそのまま表示するとHTMLのタグなどに解釈されて表示が崩れるため、エンティティ参照に変換する処理のことである。Webアプリでは、入力ミスもそうだが、悪意のある第三者が入力する可能性もあるので、エスケープ処理を行うなどして、プログラムの誤動作を防ぐようにしなければならない。
このあとがバリデーション(validation)である。ここでいうバリデーションとは、入力されたデータが期待通りの値であることを検証する処理を指す。
ここでは、変数a, bが整数(0,1,2‥‥9の文字)であること、a<b であることの2つを検証する。2つの検証をクリアしたらforループに入る、それ以外ならエラーメッセージを表示して計算しないようにする。
26: /**
27: * 計算と画面表示
28: */
29: function execute() {
30: //変数宣言
31: let a = document.getElementById('from').value; //開始値
32: let b = document.getElementById('to').value; //終了値
33: let n = ''; //合計値
34: let errmsg = ''; //エラーメッセージ
35:
36: //空白除去
37: a = a.trim();
38: b = b.trim();
39:
40: //入力文字のエスケープ
41: a = specialCharacters2HTMLentities(a);
42: b = specialCharacters2HTMLentities(b);
43:
44: //バリデーション
45: if (a.length === 0) {
46: errmsg = '開始値に整数を入力してください';
47: } else if (b.length === 0) {
48: errmsg = '終了値に整数を入力してください';
49: } else if (! a.match(/^[0-9]+$/gi)) {
50: errmsg = '"' + a + '" は整数ではありません';
51: } else if (! b.match(/^[0-9]+$/gi)) {
52: errmsg = '"' + b + '" は整数ではありません';
53: } else {
54: a = parseInt(a, 10); //10進整数化
55: b = parseInt(b, 10); //10進整数化
56: if (a >= b) {
57: errmsg = b + 'は' + a + 'より大きな整数にしてください';
58: } else {
59: //aからbまでの整数の合計を求める
60: n = 0;
61: for (let i = a; i <= b; i++) {
62: n += i;
63: }
64: }
65: }
66: //エラー・メッセージがあれば整形
67: if (errmsg !== '') {
68: errmsg = 'エラー:' + errmsg + '.';
69: }
70:
71: //結果を表示する
72: document.getElementById('sum').innerHTML = n;
73: document.getElementById('error').innerHTML = errmsg;
74: }
また、エスケープを行うユーザー関数 htmlspecialchars を用意した。
"if (a.length === 0) {...}" は、変数aに入っている文字列の長さが0、つまり何も文字が入っていないとき(空文字)はブロック内を実行するという if文)である。
そうでなく、"else if (b.length === 0) {...}" は、変数bに入っている文字列の長さが0、つまり何も文字が入っていないとき(空文字)はブロック内を実行するという else if文)である。
そうでなく、"else if (! a.match(/^\[0-9\]+$/gi)) {...}" は、変数aに0,1,2‥‥9以外の文字が含まれていないとき、ブロック内を実行するという if文)である。
そうでなく、"else if (! b.match(/^\[0-9\]+$/gi)) {...}" は、変数bに0,1,2‥‥9以外の文字が含まれていないとき、ブロック内を実行するという else if文)である。
上記いずれでもない場合は "else {...}" のブロックを実行する。
さらに elseブロックの中で、a<b を検証するif文が入れ子になっており、そのelse文のブロック(すべてのverificationをクリアした場合)に、計算のためのforループがある。
このようにif~else文やforループは、ブロックの中に入れ子にすることができる。詳しいことは、プログラムの制御で説明する。
matchメソッドは正規表現のマッチング結果を受け取るもので、これも追々解説する。
10: /**
11: * 特殊文字をHTMLエンティティに変換する
12: * @param string str 文字列
13: * @return string 変換後文字列
14: */
15: function specialCharacters2HTMLentities(str) {
16: let ss = str + '';
17: ss = ss.replace(/&/g, '&');
18: ss = ss.replace(/"/g, '"');
19: ss = ss.replace(/'/g, ''');
20: ss = ss.replace(/</g, '<');
21: ss = ss.replace(/>/g, '>');
22:
23: return ss;
24: }
コラム:エスケープ処理
<script>window.open("/");</script>――という文字列を入力すると、エラーを表示した途端、ぱふぅ家のホームページへジャンプさせることができてしまう場合がある。
今回はHTMLの特殊文字をエンティティに変換するだけだが、データベースにアクセスするようなときにはSQLインジェクションにも配慮する必要がある。
人間が入力したデータや他システムから送られてくるデータは、かならずしも予期したとおりのものでないことがある。入力に対しては、かならず、バリデーション(エスケープ処理を含む)を組み込む習慣を身につけよう。