JavaScript 関数を AJAX で仕入れて実行する。関数の API 化というアイデア
最近、サーバレスアーキテクチャの一環で、FaaS (Function as a Service) とかいって、特定の関数を必要な時にだけ WebAPI 経由で実行する、というのが流行っているらしい。AWS Lambda ってのが有名。
ひとかたまりの処理を関数としてまとめ、それをクラウドサービスに切り出しておくことで、汎用的な処理を共通化し、アプリケーションと疎結合にできるワケだ。
この概念を聞いてから、「フロントエンドでも同じようなことはできないだろうか?」ということをぼんやり考えていた。例えば文字数チェックみたいな処理って、一度作ったら使い回しが利くものだ。それらを毎回 npm パッケージとしてインストールしてアプリにバンドルするのって、同じ関数の「コピー」があちこちのアプリに含まれているような状態で、なんだかなぁ、と思ったワケだ。上手いこと関数の CDN 化みたいなことはできないかな?と考えていた。
で、実用性はともかく、JavaScript の機能を利用すれば、それっぽいことは不可能ではないかな?と思ったのでメモする。
目次
関数を提供するサーバ側
まずは、汎用的な関数を提供するサーバを用意する。http://example.com/check-min-length
に GET 通信すると、以下のような JavaScript 関数の文字列を返してくれるとする。
function(str, minLength) {
return str.length < minLength;
}
第1引数で指定した文字列が、第2引数で指定した最低文字数を超えているかどうかを Boolean で返すような関数だ。
関数を受け取って利用するクライアントアプリ側
次に、その関数を使うアプリ側の初期処理として、関数の文字列を取得して、それを関数として定義しようと思う。
// 関数をキャッシュするオブジェクト
const myFunctions = {};
// HTTP 通信で関数の文字列を取得する擬似コード。ココらへんは適宜 AJAX 通信するライブラリに読み替えて…
HttpClient.get('http://example.com/check-min-length')
.then((funcStr) => {
// 変数 funcStr は HTTP レスポンス。関数の文字列が取得できているとする
// 関数の文字列を関数に変換し、オブジェクトにセットする
myFunctions.checkMinLength = Function.call(null, 'return ' + funcStr)();
});
ココで重要なのは、Function.call(null, 'return ' + checkMinLengthFuncStr)()
という部分。コレにより、文字列を関数として実行し、関数を取り出している。
どんな動きをしているか追うために、「こう書いたらこう出力される」というのを順に書いてみる。
const myFunc = Function.call(null, funcStr);
→console.log(myFunc);
function anonymous() {
function(str, minLength) {
return str.length < minLength;
}
}
変数 myFunc
に、Function.call(null, funcStr)
を代入する。コレだと、目的の関数を抱えた無名関数、という構造になってしまう。
Function.call(null, 'return ' + funcStr)
function anonymous() {
return function(str, minLength) {
return str.length < minLength;
}
}
そこで、Function.call()
の第2引数に retrun
を付与して、関数を return
する無名関数に仕立て上げる。
Function.call(null, 'return ' + funcStr)();
function(str, minLength) {
return str.length < minLength;
}
「関数を返す無名関数」にできたら、Function.call()()
の形にして、「関数を返す無名関数」を実行して、「関数」を受け取る。コレが完成形。
このようにして関数の文字列を関数として myFunctions.checkMinLength
に定義できたら、あとはコレをいつでも使えるだろう。
// myStr が5文字未満の場合は…
if(myFunctions.checkMinLength(myStr, 5)) {
// 何か処理する
}
こんな感じ。
実質的に eval
だよね…
さて、ココまでやってみたものの、コレって、どこかの URL から JavaScript コードを拾ってきてそれを実行するワケで、メチャクチャ危険なセキュリティホールになりそう。
関数を提供する API の URL や通信先を偽装したりできれば、任意の関数を実行させられることになるし、ココらへんのセキュリティをどう担保したらいいやら。
FaaS の考え方と違うのは、API サーバは関数のテキストをレスポンスさえできれば良く、計算リソースはクライアントに委ねられるのがメリットかなと思うんだけど、実質 eval
なので危険すぎるか…。