Edge Microgateway v. 2.5.x
対象
このトピックは、カスタム プラグインを作成して Edge Microgateway の機能を拡張するデベロッパーを対象としています。新しいプラグインを作成するには、JavaScript と Node.js の使用経験が必要です。
カスタム Edge Microgateway プラグインとは
プラグインは、Edge Microgateway に機能を追加する Node.js モジュールです。プラグイン モジュールは一貫したパターンに従い、Edge Microgateway によって認識される場所に格納されることにより、自動的に検出されて実行されます。Edge Microgateway をインストールすると、事前定義されたプラグインがいくつか提供されます。たとえば、認証、Spike Arrest、割り当て、分析用などのプラグインがあります。これらの既存のプラグインについては、プラグインの使用をご覧ください。
カスタム プラグインを作成することで、マイクロゲートウェイに新しい機能を追加できます。デフォルトの Edge Microgateway は、基本的にはターゲット サービス間でリクエストとレスポンスを変更なしで通過させる安全なパススルー プロキシです。カスタム プラグインを使用すると、マイクロゲートウェイを通過するリクエストおよびレスポンスをプログラムで操作できます。
カスタム プラグイン コードを配置する場所
カスタム プラグイン用のフォルダは、Edge Microgateway インストールの次の場所にあります。
[prefix]/lib/node_modules/edgemicro/node_modules/microgateway-plugins
Edge Microgateway をインストールするの「Edge Microgateway がインストールされる場所」の説明にあるとおり、[prefix]
は npm
接頭辞ディレクトリです。
このデフォルトのプラグイン ディレクトリは変更できます。プラグインのある場所をご覧ください。
事前定義されたプラグインを確認する
独自のプラグインを開発する前に、事前定義されたプラグインで要件を満たせないかどうかを確認することをおすすめします。事前定義されたプラグインは次の場所にあります。
[prefix]/lib/node_modules/edgemicro/node_modules/microgateway-plugins
[prefix]
は npm
接頭辞ディレクトリです。Edge Microgateway をインストールするの「Edge Microgateway がインストールされる場所」もご覧ください。
詳細については、Edge Microgateway 付属の事前定義されたプラグインもご覧ください。
単純なプラグインを作成する
このセクションでは、単純なプラグインを作成するために必要な手順について説明します。このプラグインは、レスポンス データを(内容にかかわらず)文字列 "Hello, World!" で上書きして、デバイスに出力します。
- Edge Microgateway が実行中の場合は、すぐに停止します。
edgemicro stop
-
cd
でカスタム プラグイン ディレクトリに移動します。cd [prefix]/lib/node_modules/edgemicro/plugins
Edge Microgateway をインストールするの「Edge Microgateway がインストールされる場所」の説明にあるとおり、
[prefix]
はnpm
接頭辞ディレクトリです。 - response-override という名前の新しいプラグイン プロジェクトを作成し、
cd
でその場所に移動します。
mkdir response-override && cd response-override
- 新しい Node.js プロジェクトを作成します。
npm init
Enter キーを何回か押して、デフォルト値を受け入れます。 - テキスト エディタを使用して、
index.js
という名前の新しいファイルを作成します。 - 次のコードを
index.js
にコピーし、ファイルを保存します。
'use strict'; var debug = require('debug') module.exports.init = function(config, logger, stats) { return { ondata_response: function(req, res, data, next) { debug('***** plugin ondata_response'); next(null, null); }, onend_response: function(req, res, data, next) { debug('***** plugin onend_response'); next(null, "Hello, World!\n\n"); } }; }
- これでプラグインが作成されたので、次は Edge Microgateway 構成に追加する必要があります。
$HOME/.edgemicro/[org]-[env]-config.yaml
ファイルを開きます。ここで、org
とenv
は Edge の組織名と環境名です。 - 次に示すように、
response-override
プラグインをplugins:sequence
要素に追加します。
... plugins: dir: ../plugins sequence: - oauth - response-override ...
- Edge Microgateway を再起動します。
- Edge Microgateway を介して API を呼び出します。(この API 呼び出しは、Edge Microgateway の設定と構成で説明しているとおり、API キー セキュリティに関するチュートリアルと同じ構成を設定していることを前提としています。)
curl -H 'x-api-key: uAM4gBSb6YoMvTHfx5lXJizYIpr5Jd' http://localhost:8000/hello/echo Hello, World!
プラグインの詳細
次の Edge Microgateway サンプル プラグインは、独自のプラグインを開発する際のパターンを示しています。このセクションで説明するサンプル プラグインのソースコードは、plugins/header-uppercase/index.js.
にあります。
- プラグインは、ルートフォルダに
package.json
とindex.js
がある標準的な NPM モジュールです。 - プラグインは init() 関数をエクスポートする必要があります。
- init() 関数には、config、logger、stats の 3 つの引数があります。これらの引数は、プラグインの init() 関数の引数に記述されています。
- init() は、リクエストの存続期間中に特定のイベントが発生したときに呼び出される、名前付き関数ハンドラを持つオブジェクトを返します。
イベント ハンドラ関数
プラグインには、これらのイベント ハンドラ関数の一部またはすべてを実装する必要があります。どの関数を実装するかは必要に応じて決定します。どの関数を選択するかは任意ですが、一般的なプラグインでは少なくともこれらの関数のサブセットが実装されます。
リクエスト フローのイベント ハンドラ
次の関数は、Edge Microgateway のリクエスト イベントで呼び出されます。
onrequest
ondata_request
onend_request
onclose_request
onerror_request
onrequest
関数
クライアント リクエストの開始時に呼び出されます。この関数は、Edge Microgateway がリクエストの最初のバイトを受信したときに起動されます。この関数を使用して、リクエスト ヘッダー、URL、クエリ パラメータ、HTTP メソッドにアクセスできます。「真」の第 1 引数(エラー状態のインスタンスなど)で next を呼び出すと、リクエスト処理が停止し、ターゲット リクエストは開始されません。
例:
onrequest: function(req, res, next) { debug('plugin onrequest'); req.headers['x-foo-request-start'] = Date.now(); next(); }
ondata_request
関数
クライアントからデータのチャンクを受信したときに呼び出されます。リクエスト データをプラグイン順序内の次のプラグインに渡します。プラグイン順序内の最後のプラグインから返された値がターゲットに送信されます。次に示す典型的な使用例では、リクエスト データを変換してからターゲットに送信しています。
例:
ondata_request: function(req, res, data, next) { debug('plugin ondata_request ' + data.length); var transformed = data.toString().toUpperCase(); next(null, transformed); }
onend_request
関数
クライアントからすべてのリクエスト データを受信したときに呼び出されます。
例:
onend_request: function(req, res, data, next) { debug('plugin onend_request'); next(null, data); }
onclose_request
関数
クライアント接続が閉じられたことを示します。この関数はクライアント接続の信頼性が低い場合に使用できます。クライアントへのソケット接続が閉じられたときに呼び出されます。
例:
onclose_request: function(req, res, next) { debug('plugin onclose_request'); next(); }
onerror_request
関数
クライアント リクエストの受信中にエラーが発生したときに呼び出されます。
例:
onerror_request: function(req, res, err, next) { debug('plugin onerror_request ' + err); next(); }
レスポンス フローのイベント ハンドラ
次の関数は、Edge Microgateway のレスポンス イベントで呼び出されます。
onresponse
ondata_response
onend_response
onclose_response
onerror_response
onresponse
関数
ターゲット レスポンスの開始時に呼び出されます。この関数は、Edge Microgateway がレスポンスの最初のバイトを受信したときに起動されます。この関数を使用して、レスポンス ヘッダーとステータス コードにアクセスできます。
例:
onresponse: function(req, res, next) { debug('plugin onresponse'); res.setHeader('x-foo-response-time', Date.now() - req.headers['x-foo-request-start']) next(); }
ondata_response
関数
ターゲットからデータのチャンクを受信したときに呼び出されます。
例:
ondata_response: function(req, res, data, next) { debug('plugin ondata_response ' + data.length); var transformed = data.toString().toUpperCase(); next(null, transformed); }
onend_response
関数
ターゲットからすべてのリクエスト データを受信したときに呼び出されます。
例:
onend_response: function(req, res, data, next) { debug('plugin onend_response'); next(null, data); }
onclose_response
関数
ターゲット接続が閉じられたことを示します。この関数はターゲット接続の信頼性が低い場合に使用できます。ターゲットへのソケット接続が閉じられたときに呼び出されます。
例:
onclose_response: function(req, res, next) { debug('plugin onclose_response'); next(); }
onerror_response
関数
ターゲット レスポンスの受信中にエラーが発生したときに呼び出されます。
例:
onerror_response: function(req, res, err, next) { debug('plugin onerror_response ' + err); next(); }
プラグイン イベント ハンドラ関数について知っておくべきこと
プラグイン イベント ハンドラ関数は、Edge Microgateway が特定の API リクエストを処理している間に発生する特定のイベントに応じて呼び出されます。
- 個々の init() 関数ハンドラ(ondata_request、ondata_response など)では、処理が完了したときに next() コールバックを呼び出す必要があります。next() を呼び出さないと、処理が停止し、リクエストはハングします。
- next() に渡す第 1 引数には、リクエスト処理を終了させるエラーを指定できます。
- ondata_ および onend_ ハンドラでは、ターゲットまたはクライアントに渡されるデータを含む第 2 引数で next() を呼び出す必要があります。この引数には、プラグインがバッファリングしていてその時点で変換するのに十分なデータがない場合は、null を指定できます。
- すべてのリクエストとレスポンスはプラグインの単一インスタンスで処理されます。プラグインで呼び出し間のリクエストごとの状態を保持したい場合、プラグインは与えられたリクエスト オブジェクト(req)に追加されるプロパティにその状態を保存できます。このオブジェクトの存続期間は API 呼び出しの期間です。
- すべてのエラーを入念に捕捉し、エラーで next() を呼び出してください。next() の呼び出しに失敗すると、API 呼び出しがハングします。
- Edge Microgateway の全体的なパフォーマンスに影響を及ぼし、メモリ不足でクラッシュを引き起こすようなメモリリークが発生しないように注意してください。
- Node.js モデルに従い、メインスレッドでコンピューティング負荷の高いタスクを実行しないように注意してください。Edge Microgateway のパフォーマンスに悪影響を及ぼす可能性があるためです。
プラグインの init() 関数について
このセクションでは、init() 関数に渡される引数 config、logger、stats について説明します。
config
Edge Microgateway の構成ファイルに、Apigee Edge からダウンロードした情報(プロダクトや割り当てなど)をマージした後に取得される構成オブジェクト。プラグイン固有の構成は config.<plugin-name>
オブジェクトに保持されます。
foo という値を持つ param という名前の config パラメータを response-override という名前のプラグインに追加するには、default.yaml
ファイルに以下の行を挿入します。
response-override: param: foo
これにより、次のようにしてプラグイン コードでこのパラメータにアクセスできます。
// Called when response data is received ondata_response: function(req, res, data, next) { debug('***** plugin ondata_response'); debug('***** plugin ondata_response: config.param: ' + config.param); next(null, data); },
この場合、プラグインのデバッグ出力に foo が表示されます。
Sun, 13 Dec 2015 21:25:08 GMT plugin:response-override ***** plugin ondata_response: config.param: foo
logger
システムロガーです。現在使用されているロガーは、次の関数をエクスポートします。object には、文字列、HTTP リクエスト、HTTP レスポンス、エラー インスタンスを指定できます。
info(object, message)
warn(object, message)
error(object, message)
stats
リクエスト、レスポンス、エラーの件数や、マイクロゲートウェイ インスタンスを通過するリクエストとレスポンスに関連するその他の集計統計情報を保持するオブジェクト。
- treqErrors - エラーになったターゲット リクエストの数。
- treqErrors - エラーになったターゲット レスポンスの数。
- statusCodes - レスポンス コードの発生数を含むオブジェクト:
{ 1: number of target responses with 1xx response codes 2: number of target responses with 2xx response codes 3: number of target responses with 3xx response codes 4: number of target responses with 4xx response codes 5: number of target responses with 5xx response codes }
- requests - リクエストの総数。
- responses - レスポンスの総数。
- connections - アクティブなターゲット接続の数。
next() 関数について
すべてのプラグイン メソッドは、next()
を呼び出して後続のメソッドの処理につなげる必要があります(そのようにしないとプラグイン プロセスがハングします)。リクエストのライフサイクルで最初に呼び出されるメソッドは、onrequest() です。次に呼び出されるメソッドは ondata_request()
メソッドです。ただし、ondata_request
はリクエストにデータが含まれる場合(たとえば POST リクエストの場合)にのみ呼び出されます。次に呼び出されるメソッドは onend_request()
で、これはリクエスト処理が完了したときに呼び出されます。onerror_*
関数はエラーが発生した場合にのみ呼び出され、必要に応じてカスタムコードでエラーを処理できます。
リクエストでデータが送信され、ondata_request()
が呼び出されたとします。この関数は 2 つのパラメータで next()
を呼び出します。
next(null, data);
慣例では、最初のパラメータはエラー情報を伝達するために使用します。エラー情報はチェーン内の後続の関数で処理できます。これを「偽」の引数である null
に設定すると、エラーはなく、通常のリクエスト処理を進める必要があるという意味になります。この引数が「真」(エラー オブジェクトなど)の場合、リクエスト処理は停止し、リクエストはターゲットに送信されます。
2 番目のパラメータは、リクエスト データをチェーン内の次の関数に渡します。追加の処理を行わない場合、リクエスト データは変更されずに API のターゲットに渡されます。ただし、このメソッド内でリクエスト データを変更し、変更されたリクエストをターゲットに渡すこともできます。たとえば、リクエスト データが XML でターゲットが JSON を予期している場合、リクエスト ヘッダーの Content-Type を application/json
に変更し、ご希望の方法(たとえば、NPM から取得した Node.js の xml2json コンバータ)でリクエスト データを JSON に変換するコードを ondata_request()
メソッドに追加できます。
コードの例を見てみましょう。
ondata_request: function(req, res, data, next) { debug('****** plugin ondata_request'); var translated_data = parser.toJson(data); next(null, translated_data); },
この場合、リクエスト データ(XML と仮定)は JSON に変換され、変換されたデータは、バックエンド ターゲットに渡される前に、next()
によってリクエスト チェーン内の次の関数に渡されます。
デバッグする場合は、変換されたデータを表示するデバッグ ステートメントを追加できます。次に例を示します。
ondata_request: function(req, res, data, next) { debug('****** plugin ondata_request'); var translated_data = parser.toJson(data); debug('****** plugin ondata_response: translated_json: ' + translated_json); next(null, translated_data); },
プラグイン ハンドラの実行順序について
Edge Microgateway 用のプラグインを作成する場合、プラグイン イベント ハンドラの実行順序を理解する必要があります。
重要なポイントは、Edge Microgateway 構成ファイルでプラグイン順序を指定すると、リクエスト ハンドラは昇順で実行され、レスポンス ハンドラは降順で実行されることです。
次の例は、この実行順序を理解するのに役立ちます。
1. 3 つの単純なプラグインを作成する
次のようなプラグインを検討します。このプラグインは、イベント ハンドラが呼び出されたときにコンソール出力を表示するという単純なものです。
plugins/plugin-1/index.js
module.exports.init = function(config, logger, stats) { return { onrequest: function(req, res, next) { console.log('plugin-1: onrequest'); next(); }, onend_request: function(req, res, data, next) { console.log('plugin-1: onend_request'); next(null, data); }, ondata_response: function(req, res, data, next) { console.log('plugin-1: ondata_response ' + data.length); next(null, data); }, onend_response: function(req, res, data, next) { console.log('plugin-1: onend_response'); next(null, data); } }; }
次に、別の 2 つのプラグイン plugin-2
および plugin-3
を作成してみましょう。コードはほぼ同じですが、console.log()
ステートメントを plugin-2
と plugin-3
でそれぞれ変更します。
2. プラグイン コードを確認する
<microgateway-root-dir>/plugins/plugin-1/index.js
にエクスポートされたプラグイン関数は、リクエストとレスポンスの処理中に特定のタイミングで実行されるイベント ハンドラです。たとえば、リクエスト ヘッダーの最初のバイトが受信されると、onrequest
が実行されます。一方、レスポンス データの最後のバイトが受信されると、onend_response
が実行されます。
ondata_response ハンドラを見てみましょう。このハンドラは、レスポンス データのチャンクが 1 つ受信されるたびに呼び出されます。重要なのは、レスポンス データが一度にすべて受信されるとは限らないことです。むしろ、データは任意の長さのチャンクとして受信されます。
3. プラグイン順序にプラグインを追加する
この例では、次のようにして Edge Microgateway 構成ファイル(~./edgemicro/config.yaml
)のプラグイン順序にプラグインを追加します。この場合の順序は重要です。それによって、プラグイン ハンドラが実行される順序が定義されます。
plugins: dir: ../plugins sequence: - plugin-1 - plugin-2 - plugin-3
4. デバッグ出力を調べる
それでは、プラグインが呼び出されたときに生成される出力を見てみましょう。注意すべき重要な点がいくつかあります。
- Edge Microgateway 構成ファイル(
~./edgemicro/config.yaml
)のプラグイン順序は、イベント ハンドラが呼び出される順序を指定します。 - リクエスト ハンドラは、昇順(プラグイン順序に表示される順序 - 1、2、3 の順)で呼び出されます。
- レスポンス ハンドラは降順(3、2、1 の順)で呼び出されます。
ondata_response
ハンドラは、到着するデータのチャンク 1 つにつき 1 回呼び出されます。この例(以下に示す出力)では、2 つのチャンクが受信されます。
これらの 3 つのプラグインが使用されている場合に、Edge Microgateway 経由でリクエストが送信されたときに生成されるサンプル デバッグ出力を次に示します。ハンドラが呼び出される順序に注意してください。
plugin-1: onrequest plugin-2: onrequest plugin-3: onrequest plugin-1: onend_request plugin-2: onend_request plugin-3: onend_request plugin-3: ondata_response 931 plugin-2: ondata_response 931 plugin-1: ondata_response 931 plugin-3: ondata_response 1808 plugin-3: onend_response plugin-2: ondata_response 1808 plugin-2: onend_response plugin-1: ondata_response 1808 plugin-1: onend_response
概要
リクエストまたはレスポンス データの蓄積や変換などのカスタム プラグイン機能を実装する場合、プラグイン ハンドラの呼び出し順序を理解することはとても重要です。
リクエスト ハンドラは Edge Microgateway 構成ファイルでプラグインが指定されている順序で実行され、レスポンス ハンドラは逆の順序で実行されることを忘れないでください。
プラグインでのグローバル変数の使用について
Edge Microgateway へのリクエストはすべて、プラグインの同じインスタンスに送信されます。したがって、別のクライアントからの 2 番目のリクエストの状態により、最初のリクエストの状態が上書きされます。プラグインの状態を保存する唯一の安全な方法は、リクエストまたはレスポンス オブジェクト(その存続期間はリクエストの存続期間に制限されます)のプロパティに状態を格納することです。
プラグイン内のターゲット URL の書き換え
v2.3.3 で追加
プラグイン コードで変数 req.targetHostname および req.targetPath を変更すると、プラグインのデフォルト ターゲット URL を動的にオーバーライドできます。
v2.4.x で追加
ターゲットのエンドポイント ポートをオーバーライドして、HTTP と HTTPS のどちらかを選択することもできます。そのためには、プラグイン コードで変数 req.targetPort および req.targetSecure を変更します。HTTPS を選択する場合は、req.targetSecure を true に設定します。HTTP の場合は false に設定します。req.targetSecure を true に設定する場合の詳細については、このディスカッション スレッドをご覧ください。
サンプル プラグイン
これらのプラグインは、Edge Microgateway インストールに付属しています。Edge Microgateway インストールの次の場所にあります。
[prefix]/lib/node_modules/edgemicro/plugins
Edge Microgateway をインストールするの「Edge Microgateway がインストールされる場所」の説明にあるとおり、[prefix]
は npm
接頭辞ディレクトリです。
accumulate-request
このプラグインは、クライアントからのデータチャンクを、リクエスト オブジェクトに添付された配列プロパティに蓄積します。すべてのリクエスト データが受信されると、配列はバッファに連結され、プラグイン順序内の次のプラグインにそのバッファが渡されます。このプラグインは、後続のプラグインが蓄積されたリクエスト データを受け取れるように、プラグイン順序内の最初のプラグインにする必要があります。
module.exports.init = function(config, logger, stats) { function accumulate(req, data) { if (!req._chunks) req._chunks = []; req._chunks.push(data); } return { ondata_request: function(req, res, data, next) { if (data && data.length > 0) accumulate(req, data); next(null, null); }, onend_request: function(req, res, data, next) { if (data && data.length > 0) accumulate(req, data); var content = null; if (req._chunks && req._chunks.length) { content = Buffer.concat(req._chunks); } delete req._chunks; next(null, content); } }; }
accumulate-response
このプラグインは、ターゲットからのデータチャンクを、レスポンス オブジェクトに添付された配列プロパティに蓄積します。すべてのレスポンスト データが受信されると、配列はバッファに連結され、プラグイン順序内の次のプラグインにそのバッファが渡されます。このプラグインは、逆の順序で処理されるレスポンスで動作するため、プラグイン順序の最後に配置する必要があります。
module.exports.init = function(config, logger, stats) { function accumulate(res, data) { if (!res._chunks) res._chunks = []; res._chunks.push(data); } return { ondata_response: function(req, res, data, next) { if (data && data.length > 0) accumulate(res, data); next(null, null); }, onend_response: function(req, res, data, next) { if (data && data.length > 0) accumulate(res, data); var content = Buffer.concat(res._chunks); delete res._chunks; next(null, content); } }; }
header-uppercase プラグイン
Edge Microgateway ディストリビューションには、<microgateway-root-dir>/plugins/header-uppercase
というサンプル プラグインが付属しています。このサンプルには、各関数ハンドラに関するコメントが含まれています。このサンプルは、ターゲット レスポンスの単純なデータ変換を行い、カスタム ヘッダーをクライアント リクエストとターゲット レスポンスに追加します。
<microgateway-root-dir>/plugins/header-uppercase/index.js
のソースコードを次に示します。
'use strict'; var debug = require('debug')('plugin:header-uppercase'); // required module.exports.init = function(config, logger, stats) { var counter = 0; return { // indicates start of client request // request headers, url, query params, method should be available at this time // request processing stops (and a target request is not initiated) if // next is called with a truthy first argument (an instance of Error, for example) onrequest: function(req, res, next) { debug('plugin onrequest'); req.headers['x-foo-request-id'] = counter++; req.headers['x-foo-request-start'] = Date.now(); next(); }, // indicates start of target response // response headers and status code should be available at this time onresponse: function(req, res, next) { debug('plugin onresponse'); res.setHeader('x-foo-response-id', req.headers['x-foo-request-id']); res.setHeader('x-foo-response-time', Date.now() - req.headers['x-foo-request-start']); next(); }, // chunk of request body data received from client // should return (potentially) transformed data for next plugin in chain // the returned value from the last plugin in the chain is written to the target ondata_request: function(req, res, data, next) { debug('plugin ondata_request ' + data.length); var transformed = data.toString().toUpperCase(); next(null, transformed); }, // chunk of response body data received from target // should return (potentially) transformed data for next plugin in chain // the returned value from the last plugin in the chain is written to the client ondata_response: function(req, res, data, next) { debug('plugin ondata_response ' + data.length); var transformed = data.toString().toUpperCase(); next(null, transformed); }, // indicates end of client request onend_request: function(req, res, data, next) { debug('plugin onend_request'); next(null, data); }, // indicates end of target response onend_response: function(req, res, data, next) { debug('plugin onend_response'); next(null, data); }, // error receiving client request onerror_request: function(req, res, err, next) { debug('plugin onerror_request ' + err); next(); }, // error receiving target response onerror_response: function(req, res, err, next) { debug('plugin onerror_response ' + err); next(); }, // indicates client connection closed onclose_request: function(req, res, next) { debug('plugin onclose_request'); next(); }, // indicates target connection closed onclose_response: function(req, res, next) { debug('plugin onclose_response'); next(); } }; }
transform-uppercase
これは、一般的な変換プラグインです。必要に応じて変更することで、さまざまな種類の変換を実行できます。この例では、単にレスポンスとリクエストのデータを大文字に変換しています。
*/ module.exports.init = function(config, logger, stats) { // perform content transformation here // the result of the transformation must be another Buffer function transform(data) { return new Buffer(data.toString().toUpperCase()); } return { ondata_response: function(req, res, data, next) { // transform each chunk as it is received next(null, data ? transform(data) : null); }, onend_response: function(req, res, data, next) { // transform accumulated data, if any next(null, data ? transform(data) : null); }, ondata_request: function(req, res, data, next) { // transform each chunk as it is received next(null, data ? transform(data) : null); }, onend_request: function(req, res, data, next) { // transform accumulated data, if any next(null, data ? transform(data) : null); } }; }
その他のプラグイン チュートリアル
Apigee ブログにある次のチュートリアルもご覧ください。