やりたいこと
複数の要素を返すバックエンドAPIの応答から、クライアントの指定した要素のみを抽出して返すAPIゲートウェイを構成する方法を考えてみます。例えば、GET /accounts/1001?mask=name,age
のように、変数(ここでは"mask")に項目名を指定することで、クライアント側で必要な項目だけを返すAPIを作ってみます。
{
"id" : "1001",
"name" : "alice",
"phone" : "000-000-0000",
"gender" : "female",
"age" : 20
}
つまり、これをこうしたい。
{
"name" : "alice",
"age" : 20
}
前提、環境等
- 2017/07/26 時点の IBM API Connect on Bluemix Public で動作を確認しています
実装
実装方針
IBM API Connectには様々なメッセージ変換部品が用意されていますが、今回はポリシーを使ってみます。GatewayScriptポリシーの詳細はマニュアルを確認してください。
処理の流れ
処理の流れはこんな感じになります。
1. リクエスト・パラメートしてmask
を受け付ける
2. Invoke
ポリシーでバックエンドAPIからアカウント情報のJSONを取得
3. GatewayScript
ポリシーでmask
の内容に応じてアカウント情報をマスキング
3.1 Invoke
の結果を変数に読み込む
3.2 mask
パラメータをカンマで分割して変数に読み込む
3.3 Invoke
の結果から、mask
で指定された項目のみをコピーしたオブジェクトを組み立て
3.4 組み立てたオブジェクトを応答に書き出し
アセンブリー・フロー実装
invoke部分は適当なバックエンドを呼ぶように構成してください。また今回のサンプルではResponse Object Variableが指定されていない前提でmask-outputが書かれていますので、空であることを確認します。
GatewayScript実装
今回の主役であるmask-output
のコードはこんな感じです。
apim.readInputAsJSON(function (error,inObj) {
// 前ステップ(invoke)の出力をinObjに読み込む
if (error) {
console.log("Failed to read data in JSON format");
} else {
var maskStr = apim.getvariable('request.parameters.mask');
if (maskStr) {
// maskパラメータが存在する場合のみ、マスキングを実行
var maskAry = maskStr.split(',');
var outObj = {};
for(let mask of maskAry){
// maskに指定された項目をoutObjにコピー
if(inObj.hasOwnProperty(mask)){
outObj[mask] = inObj[mask];
}
}
// outObjの書き出し処理
session.output.write(outObj);
apim.output('application/json');
}
}
});
実行結果
API Managerのテストツールからテストしてみます。
maskパラメータを指定しない場合
mask=name,ageと指定した場合
コード
今回作成したAPI定義はこちらです。target-url: "set-your-url-here"
を適当な値に置き換えてご利用ください。
---
swagger: "2.0"
info:
x-ibm-name: "accounts-masked"
title: "accounts-masked"
version: "1.0.0"
schemes:
- "https"
host: "$(catalog.host)"
basePath: "/accounts-masked"
consumes:
- "application/json"
produces:
- "application/json"
securityDefinitions:
clientIdHeader:
type: "apiKey"
in: "header"
name: "X-IBM-Client-Id"
security:
- clientIdHeader: []
x-ibm-configuration:
testable: true
enforced: true
cors:
enabled: true
assembly:
execute:
- invoke:
target-url: "set-your-url-here"
- gatewayscript:
title: "mask-output"
version: "1.0.0"
source: "apim.readInputAsJSON(function (error,inObj) { // 前ステップ(invoke)の出\
力をinObjに読み込む\r\n if (error) {\r\n console.log(\"Failed to read data\
\ in JSON format\");\r\n } else {\r\n var maskStr = apim.getvariable('request.parameters.mask');\r\
\n if (maskStr) { // maskパラメータが存在する場合のみ、マスキングを実行\r\n var maskAry\
\ = maskStr.split(',');\r\n var outObj = {};\r\n\r\n for(let mask\
\ of maskAry){ // maskに指定された項目をoutObjにコピー\r\n if(inObj.hasOwnProperty(mask)){\r\
\n outObj[mask] = inObj[mask];\r\n }\r\n }\r\n\r\n\
\ // outObjの書き出し処理\r\n session.output.write(outObj);\r\n \
\ apim.output('application/json');\r\n }\r\n }\r\n});"
phase: "realized"
paths:
/accounts/{id}:
get:
responses:
200:
description: "200 OK"
parameters:
- name: "mask"
type: "string"
required: false
in: "query"
description: "Keys to be passed to client, in comma separated value format."
parameters:
- name: "id"
type: "string"
required: true
in: "path"
description: "user id"
definitions:
userInfo:
description: ""
type: "object"
properties:
id:
type: "string"
name:
type: "string"
phone:
type: "string"
gender:
type: "string"
age:
type: "number"
example: "{\r\n\"id\": \"1001\",\r\n\"name\": \"alice\",\r\n\"phone\": \"000-000-0000\"\
,\r\n\"gender\": \"female\",\r\n\"age\": 20\r\n}"
additionalProperties: true
tags: []