JavaScript での HTTP クライアントの実装

このクックブック サンプルでは、プロキシに JavaScript HTTP クライアントを接続してマッシュアップを作成する方法について説明します。この便利なパターンを利用すると、JavaScript で複数のバックエンド ターゲットからの結果を組み合わせて、リクエストとレスポンスを処理できます。

HTTP クライアント パターンの概要については、API プロキシ クックブック パターンで「JavaScript HTTP クライアント パターン」をご覧ください。

サンプルコードをダウンロードして使用する

このクックブック サンプルについて

このクックブック サンプルは、JavaScript HTTP クライアント オブジェクトを使用して複数のバックエンド サービスを直接呼び出す API プロキシ パターンを示しています。ここに示すとおり、JavaScript HTTP クライアント コードはポリシーとして ProxyEndpoint に添付されます。この場合、パターンに TargetEndpoint は不要です。HTTP クライアントがバックエンド ターゲットとのすべての通信を処理します。このパターンの概要とその他の関連するパターンについては、API プロキシ クックブック パターンをご覧ください。

ここで説明するサンプルでは、JavaScript で記述されたカスタム HTTP クライアントを使用して、次の 2 つの別々のパブリック API からのデータをマッシュアップします。

  • Google Geocoding API: この API は、住所(1600 Amphitheatre Parkway, Mountain View, CA など)を地理座標(緯度: 37.423021、経度: 122.083739 など)に変換します。
  • Google Elevation API: この API は、地球上の場所の標高データをクエリで取得するためのシンプルなインターフェースを提供します。このサンプルでは、Geocoding API から返された座標をこの API への入力として使用します。これは、プロキシを使用して API をつなぐ一般的なパターンです。

このプロキシを介してアプリ デベロッパーに公開される API は、郵便番号と国 ID の 2 つのクエリ パラメータを受け取ります。アプリ デベロッパーは次のようにこの API を呼び出します。

$ curl "http://{myorg}-test.apigee.net/javascript-mashup-cookbook?country=us&postalcode=80503"

レスポンスは、指定された郵便番号地域の中心のジオコード化された場所(緯度 / 経度)とそのジオコード化された場所の標高との組み合わせを含む JSON オブジェクトです。

{
  "country": "us",
  "postalcode": 80503,
  "location": {
    "latitude": 40.1724007,
    "longitude": -105.1960795
  },
  "altitude": {
    "meters": 1570.249755859375,
    "feet": 5151.7380519886965
  }
}

始める前に

このクックブック トピックで説明する HTTP クライアント パターンの概要については、API プロキシ クックブック パターンで「JavaScript HTTP クライアント パターン」をご覧ください。

このクックブック サンプルを利用する前に、以下の基本的なコンセプトを理解している必要があります。

  • ポリシーの内容と、ポリシーをプロキシにアタッチする方法。このサンプルでは、JavaScript アプリケーションをラップする単一のポリシーを作成します。ポリシーの概要については、ポリシーとはをご覧ください。
  • API プロキシフローの構造。フローの構成をご覧ください。フローにより、API プロキシが複数のポリシーを実行する順序を指定できます。このサンプルでは、JavaScript ポリシーはフローの ProxyEndpoint にアタッチされます。
  • ファイル システムでの API プロキシ プロジェクトの編成方法。API プロキシ構成のリファレンスで説明されています。このクックブックのトピックでは、ローカル開発(ファイル システムベース)と、管理 UI を使用して API プロキシを開発できるクラウドベースの開発を対比して示しています。
  • ここに示すとおり、このサンプルではすぐに使える 2 つの API Platform コンポーネント、つまり変数と Apigee JavaScript オブジェクト モデルを使用します。これらのコンポーネントの詳細については、変数リファレンスJavaScript オブジェクト モデルをご覧ください。
  • JavaScript と XML に関する知識。

サンプルコードをダウンロードすると、このトピックで説明するすべてのファイルが mashup-policy-cookbook サンプル フォルダに含まれています。以降のセクションでは、サンプルコードについて詳しく説明します。

フローの概要

JavaScript コードの説明に移る前に、サンプルの API プロキシのメインフローを見てみましょう。以下に示すフロー XML は、この API プロキシの詳細と、JavaScript アプリケーションがどのようにフローに統合されるかを示しています。

GitHub からサンプルをダウンロードした場合、この API プロキシ定義は doc-samples/javascript-mashup-cookbook/apiproxy/proxies/default.xml ファイルにあります。

<ProxyEndpoint name="default">
  <Flows>
    <Flow name="default">
      <Request/>
      <Response>
        <!-- Call the JavaScript that invokes both APIs and returns a response -->
        <Step><Name>MashItUp</Name></Step>
      </Response>
    </Flow>
  </Flows>

  <HTTPProxyConnection>
    <!-- Add a base path to the proxy to distinguish from others in the environment -->
    <BasePath>/javascript-mashup-cookbook</BasePath>
    <VirtualHost>default</VirtualHost>
  </HTTPProxyConnection>
  <RouteRule name="default">
    <!-- No route. The proxy will return the request and not call a backend service. -->
  </RouteRule>
</ProxyEndpoint>

フローの要素の概要を以下に示します。

  • <Request> - この API プロキシでは、フローの <Request> 要素を使用しません。このパターンでは、リクエストは JavaScript アプリケーション内で処理されます。
  • <Response> - パイプラインのレスポンス部分では、単に JavaScript アプリケーションを呼び出します。このアプリケーションが、クライアントからのリクエストとすべてのバックエンド API 呼び出しを処理します。
  • <HttpProxyConnection> - アプリをこの API プロキシに接続する方法の詳細を指定します。ここには、この API の呼び出し方法を指定する <BasePath> が含まれます。
  • <RouteRule> - <RouteRule> について興味深い点は、これを指定しないということです。つまり、<RouteRule> で呼び出す TargetEndpoint を指定しません。このパターンでは、TargetEndpoint を呼び出す必要はありません。JavaScript アプリケーションが、クライアント アプリに返されるレスポンス オブジェクトを作成します。

JavaScript アプリケーションのコーディング

サンプルの JavaScript アプリケーションの完全なリストを以下に示します。ほとんどは、いくつかの Apigee 固有のオブジェクトと変数が混在している単純な JavaScript です。便宜上、これらのオブジェクトと変数はコード内に太字で示されています。Apigee 固有の部分と、その部分をコードの直後にアプリケーションで利用できるようにする方法について説明します。

// Initialize the response
response.content = '';
response.headers['Content-Type'] = 'application/json';

try {
   if ((request.queryParams.postalcode == undefined) ||
       (request.queryParams.country == undefined)) {
     throw '"postalcode" and "country" query parameters are required';
   }

   // Send an HTTP GET to the URL that we construct
   var geocoding = httpClient.get(
        'http://maps.googleapis.com/maps/api/geocode/json?address=' +
        request.queryParams.postalcode +
        '&region=' + request.queryParams.country +
        '&sensor=false');

    geocoding.waitForComplete();

    if (!geocoding.isSuccess()) {
        throw 'Error contacting geocoding web service';
    }

    // We got a response. Parse the JSON into a JavaScript object.
    geocodeResponse = geocoding.getResponse().content.asJSON;

    if (geocodeResponse.status != 'OK') {
        throw 'Error returned from geocoding web service: ' + geocodeResponse.status;
    }

    // Go through the JavaScript returned by Google and get the results
    var lat = geocodeResponse.results[0].geometry.location.lat;
    var lng = geocodeResponse.results[0].geometry.location.lng;

    // Send another HTTP GET to the other service
    var altitude = httpClient.get(
        'http://maps.googleapis.com/maps/api/elevation/json?sensor=false&locations=' +
            lat + ',' + lng);

    altitude.waitForComplete();

    if (!altitude.isSuccess()) {
        throw 'Error contacting altitude web service';
    }

    altitudeResponse = altitude.getResponse().content.asJSON;

    if (altitudeResponse.status != 'OK') {
        throw 'Error returned from altitude web service: ' + altitudeResponse.status;
    }

    var alt = altitudeResponse.results[0].elevation;

    // Final assembly of the JSON object

    var body = response.content.asJSON;
    body.country = request.queryParams.country;
    body.postalcode = request.queryParams.postalcode;
    body.location = { latitude: lat, longitude: lng};
    body.altitude = { meters : alt, feet : alt * 3.2808399};

} catch (err) {

    // Handle any error that may have happened previously by generating a response
    response.content.asJSON.error = err;
}
前述のとおり、このコードには Apigee 固有の変数とオブジェクトが含まれています。変数は、API プロキシ ランタイム環境を介してインクルードされます。これには、requestresponserequest.queryParamsresponse.contentresponse.headers があります。これらの変数には、アプリが API プロキシにリクエストを送信するたびに値が代入されます。変数の詳細と、変数が API プロキシ環境に関与する方法については、変数のリファレンスをご覧ください。

httpClient(Apigee の JavaScript HTTP クライアント)も Apigee 固有のオブジェクトです。このオブジェクトは Apigee JavaScript オブジェクト モデルの一部です。このオブジェクト モデルは、関連するプロパティを持つリクエスト、レスポンス、コンテキストの各オブジェクトを定義します。このサンプルの目的は、バックエンド サービス(Google API)にリクエストを行い、それらのサービスからのレスポンスを返すことです。変数と同様に、HTTP クライアントは、API プロキシにデプロイされた JavaScript アプリケーションですぐに利用できます。httpClient の詳細については、JavaScript オブジェクト モデルをご覧ください。

JavaScript ポリシーの作成

API プロキシのコンテキスト内で動作するようにするには、JavaScript アプリケーションをポリシーでラップする必要があります。プロキシはこのポリシーをアタッチ時点で参照し、すぐに JavaScript を実行します(必要に応じて、前述の「フローの概要」に戻り、このポリシーがフローにアタッチされる場所を確認してください)。

GitHub からサンプルをダウンロードした場合、このポリシーは doc-samples/javascript-mashup-cookbook/apiproxy/policies/MashItUp.xml ファイルにあります。

<JavaScript name="MashItUp" timeout="10000">
    <ResourceURL>jsc://MashItUp.js</ResourceURL>
</JavaScript>

ポリシー名と JavaScript ソースファイルへの参照のみで、非常に単純です。

サンプルのテスト

まだ行っていない場合は、javascript-mashup-cookbook サンプルをダウンロードし、デプロイして実行してみてください。このサンプルは、GitHub の Apigee Edge サンプル リポジトリの doc-samples フォルダにあります。javascript-mashup-cookbook フォルダにある README ファイルの指示に従ってください。または、サンプル API プロキシの使用の手順の概要に従ってください。

要約すると、API は次のように呼び出すことができます。{myorg} は自分の組織名に置き換えます。

$ curl "http://{myorg}-test.apigee.net/javascript-mashup-cookbook?country=us&postalcode=80503"

レスポンスは、指定された郵便番号地域の中心のジオコード化された場所(緯度 / 経度)とそのジオコード化された場所の標高との組み合わせを含む JSON オブジェクトです。また、メートルからフィートへの標高の変換も含まれます。データは 2 つのバックエンド API から取得され、JavaScript アプリケーション内でマッシュアップされた後、少し操作されてから単一のレスポンスでクライアントに返されています。

{
  "country": "us",
  "postalcode": 80503,
  "location": {
    "latitude": 40.1724007,
    "longitude": -105.1960795
  },
  "altitude": {
    "meters": 1570.249755859375,
    "feet": 5151.7380519886965
  }
}

まとめ

このクッグブックのトピックでは、JavaScript で記述された HTTP クライアントを Apigee API プロキシにアタッチする方法について説明しました。このようなパターンの用途の 1 つとして、複数のバックエンド サービスから pull されたデータのマッシュアップの作成があります。同時に、JavaScript はポリシーとして API プロキシにアタッチされるため、トラフィック管理、セキュリティ、メディエーション、分析などの Apigee Edge の機能に関与できます。