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

このクックブック サンプルでは、プロキシに JavaScript HTTP クライアントを添付して mashup を作成する方法について説明します。この便利なパターンを利用すると、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 アプリケーションを呼び出します。この 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 プロキシ環境にどのように関与するかについて詳しくは、変数のリファレンスをご覧ください。

一方、Apigee 固有のオブジェクトは httpClient(Apigee の JavaScript HTTP クライアント)です。このオブジェクトは 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 の機能に関与できます。