inuinu blog(開発用)

BOT @wagagun の開発ノウハウや、IT向け?の雑記ブログです。

素のJavascript(Vanilla)だけでフロントエンド開発(03:パラメータ編)

このページでは、ReactやVueではなく、Vanillaと呼ばれる素のJavascriptのみで、フロントエンド開発が可能であるか?を解説します。今回はGET/POST等のパラメータについて。

今回は、前回…

inuinu-tech.hatenablog.jp

の続きになります。

いきなりここにたどり着いたのでしたら、お手すきの際で構いませんので、下記の記事からも読んでみていただければ幸いです。

inuinu-tech.hatenablog.jp

今回の課題のゴールは?

今回は、パラメータの取得方法です。

Javascriptで取得できるパラメータについて

そもそもパラメータって何に使用するの?

https://〜/user.htm?id=123」といったように「?」のつくURLって結構見かけますよね?

この場合は、idという入れ物(変数など)に「123」という値がセットされます。
つまり、123というユーザーIDを持つ者について、何らかの画面を表示しなさい!という意味になります。

URLの中にパラメータは渡せるか?

いわゆるRestfulっぽいヤツですよね?

https://〜/user/123

のように、URLの中にユーザーIDが溶け込んでいる感じのシャレオツなヤツ。

URLからパラメータを抜き取ること自体は、location.hrefを利用して、URLから解析することは可能かと思います。

しかし、今回のURLは、サーバーサイドで生成されたものではありません。
静的なディレクトリ構成では、「user/123」は実現不可能かと思いますよ。

ですので、ダサくていいのでGETパラメータでいきましょう。

JavascriptではPOSTパラメータは取得できない

HTMLの代表的な入力パラメータとして、GETの他にPOSTがありますが、POSTはどうでしょうか?

JavaScriptのみでPOSTデータを取得する方法
qiita.com

まあ、読んだ限りは無理でしょうね。

JavascriptではGETパラメータは取得できる

GETパラメータは利用できることがわかりました。

先程のlocation.hrefでゴリゴリ解析し…でも可能でしょうが、searchParamsを利用すればもっと楽に取得可能です。

GETパラメータの取得例

とりあえずのプログラム例

まずは簡単に作ってみましょう。

●test_param01.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script>
// パラメータを取得
let param = new URL(window.location.href).searchParams;

// getメソッドを裸のまま表示
console.log(param.get('a'));
console.log(param.get('b'));
console.log(param.get('c'));
</script>
</head>
<body>
</body>
</html>

こちらを
http://localhost/~(ユーザー名)/test_param01.html?a=あああ&b=123
で実行すると、Consoleには…

あああ
123
null

が表示されると思います!
ただ、nullがちょっと気になりますね。

デフォルト値をセットするようにする

パラメータが、文字ならばnullではなく空文字('')を、数字の場合は0を、省略値としてセットしたい!というニーズがあると思います。
(後続の処理を円滑にすすめるために。)<

以下はそれらを考慮した例です。

●test_param02.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script>
// パラメータを取得
let prm = new URL(window.location.href).searchParams;

// getメソッドを裸のまま
//console.log(prm.get('a'));
//console.log(prm.get('b'));
//console.log(prm.get('c'));

// 変換
prm_a = cnvDefault(prm.get('a'));
prm_b = cnvDefault(prm.get('b'), true); // 数値
prm_c = cnvDefault(prm.get('c'));

console.log(prm_a);
console.log(prm_b);
console.log(prm_c);

console.log(prm_a + 1); // 文字に加算
console.log(prm_b + 1); // 数値に加算

// デフォルト値設定
function cnvDefault(val, num=false) {
  if (num) {
    // 数値の妥当性を判断
    if (isNaN(val)) {
      val = 0;
    } else {
      val = val - 0; // - 0は数字変換のテクニック
    }    
  } else {
    // 文字はNull合体演算子で判断
    val = val ?? '';
  }
  return val;
}
</script>
</head>
<body>
</body>
</html>

先程と同様に、
http://localhost/~(ユーザー名)/test_param02.html?a=あああ
で実行すると、Consoleには…

あああ
0

あああ1
1

が表示されると思います。
文字列の場合は右に1が付加され、数値の場合はカウントアップされています。
上手くいってそうですね!

XSS攻撃を考慮したパラメータ取得に変更する

タグを構成する文字が挿入されている場合はエスケープ(サニタイジング)する

先程のhtmlを
http://localhost/~(ユーザー名)/test_param02.html?a=<input name="hoge">

で実効すると、INPUTタグがそのまま渡ってしまいますね。
これはちょっとマズいかも…

どうマズいかは、「XSS」で検索してみてください。

このXSS攻撃から身を守るためには、HTMLタグやJavascriptで使用される記号を、別の何らかの文字(列に)に変換し対処します。これをサニタイジングと言います

サニタイジングは以下の、1または2のいずれかの文字列に変換すればよいでしょう。

  1. 危険な記号を文字実体参照あるいは数値実体参照に変換する
  2. あるいは、危険な記号を全角文字に変換する

サーバーサイドスクリプト(PHPなど)では標準的に備わっていたりしますので、実体参照に変換する例の方がネット上では圧倒的ですが、(実体参照とは無縁な)他のデータやシステムに流用することも考えて、全角に変換することもあります

以下は不正な文字列を実体参照に変換する例です。

●test_param03.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script>
// パラメータを取得
let prm = new URL(window.location.href).searchParams;

// getメソッドを裸のまま
//console.log(prm.get('a'));
//console.log(prm.get('b'));
//console.log(prm.get('c'));

// 変換
prm_a = cnvDefault(prm.get('a'));
prm_b = cnvDefault(prm.get('b'), true); // 数値
prm_c = cnvDefault(prm.get('c'));

console.log(prm_a);
console.log(prm_b);
console.log(prm_c);

// デフォルト値設定
function cnvDefault(val, num=false) {
  if (num) {
    // 数値の妥当性を判断
    if (isNaN(val)) {
      val = 0;
    } else {
      val = val - 0; // - 0は数字変換のテクニック
    }    
  } else {
    // 文字はNull合体演算子で判断
    val = val ?? '';
    // サニタイジング
    val = cnvEscape(val);
  }
  return val;
}

// 実体参照に変換する
function cnvEscape(str){
  return String(str)
    .replace(/&/g,"&")
    .replace(/"/g,""")
    .replace(/</g,"<")
    .replace(/>/g,">")
}
</script>
</head>
<body>
</body>
</html>

http://localhost/~(ユーザー名)/test_param03.html?a=<input name="hoge">
で実行すると、Consoleには…

&lt;input name=&quot;hoge&quot;&gt;
0

と表示されると思います。

例では&とダブルクォーテーション、<>のみですが、()や#やシングルクォーテーションやセミコロンといった文字を加えた方がいいケースもあるでしょう。そういった場合は、それぞれの文字実体参照あるいは数値実体参照を確認してください。

HTML系のハウツーサイトによっては、シングルクォーテーションを&amp;apos;に変換していますが、確かそれってXMLのみで定義されていた文字実体参照だったのですが…

選択項目の場合は、デフォルト値にする

例えば、一覧画面のソートの昇順(デフォルト)を0、降順を1とするようなパラメータがあるとします。
ここに2という値をセットした場合、支障をきたすような場合に備え、パラメーターをデフォルト値に戻すようにします。

厳し目にエラーとみなし、ログイン画面に強制遷移するという手法もありますが、それですと設定値を割り出されてしまいそうですので、何食わぬ顔をして、デフォルト表示したほうが良いかもしれませんね。

実際はAPI側でソートした結果を返す…といったシチュエーションが多いでしょうから、Javascript側ではチェックせずに、API側でデフォルト値に戻す方が、処理の中身が隠蔽されると思います。
(しかし、今回はあえてJavascript側で行うことにします。)

以下は不正な選択値を考慮した例です。

fanctionが長くなってしまったので、今回は外出しします。

●test_param04.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script src="js/vanilla-front-end.js"></script>
<script>
// パラメータを取得
let prm = new URL(window.location.href).searchParams;

// getメソッドを裸のまま
//console.log(prm.get('a'));
//console.log(prm.get('b'));
//console.log(prm.get('c'));

// 変換
prm_a = cnvDefault(prm.get('a'));
prm_b = cnvDefault(prm.get('b'), true); // 数値
prm_c = cnvSelVal(prm.get('c'), ['x', 'y', 'z']);

console.log(prm_a);
console.log(prm_b);
console.log(prm_c);
</script>
</head>
<body>
</body>
</html>

jsディレクトリを作成し、以下のJavascriptソース(vanilla-front-end.js)を作成します。

他の演習を先に行い、vanilla-front-end.jsがすでに存在する場合は、追記してください。

●vanilla-front-end.js"

// デフォルト値設定
function cnvDefault(val, num=false) {
  if (num) {
    // 数値の妥当性を判断
    if (isNaN(val)) {
      val = 0;
    } else {
      val = val - 0; // - 0は数字変換のテクニック
    }    
  } else {
    // 文字はNull合体演算子で判断
    val = val ?? '';
    // サニタイジング
    val = cnvEscape(val);
  }
  return val;
} // function

// 実体参照に変換する
function cnvEscape(str){
  return String(str)
    .replace(/&/g,"&")
    .replace(/"/g,""") // "が3重だとpreタグの色がおかしくなりますのでコメントを付けています、実行時はコメントを外してください
    .replace(/</g,"<")
    .replace(/>/g,">")
} // function

// 選択項目のデフォルト値設定
function cnvSelVal(val, arr) {
  let idx = arr.indexOf(val);
  if (idx == -1) {
    return arr[0];
  } else {
    return arr[idx];
  }
} // function

http://localhost/~(ユーザー名)/test_param04.html?a=aaa&b=123&c=w
で実行すると、Consoleには…

aaa
123
x

と表示されると思います。

17行目では、パラメータcにx,y,zといった文字配列がセットされていますが、それ以外の文字列や、パラメータcが渡されない場合は、配列[0]の値「x」がセットされます。

最後に

これで、かなり実用的なソースになってきたと思います。

今回もfunctionをHTMLの中にベタ書きしましたが、実際に利用する場合は、外部のJSソースにまとめましょう。

次回は、いよいよAPIの登場です。
SELECTのOPTIONを、API経由でDBの名称テーブル等から引っ張ってきて、追加する…といった、実践編になります。

inuinu-tech.hatenablog.jp

それでは、また。