Apex Web サービスとコールアウト

摘要

このドキュメントでは、Apex での Web サービスのサポートについて説明します。Apex コードを使用してカスタムの Web サービスを作成する方法、外部の Web サービスを Force.com から直接呼び出す方法を解説するほか、具体的な活用の仕方やベストプラクティス、注意すべき点、コードサンプルなどを紹介します。

「Apex Web サービス」と「Apex コールアウト」の概要

Apex コードでは、Apex メソッドを Web サービスとして公開できます。今回のドキュメントでは、この概念を「Apex Web サービス」と呼びます。また、Apex では外部の Web サービスを呼び出すこともできます。今回のドキュメントでは、この概念を「Apex コールアウト」と呼びます。前者はクライアントから起動できる Web サービスを作成し、後者は外部の Web サービスを呼び出します。

Apex Web サービスでは、Apex メソッドを記述して Web サービスとして公開できます。これにより、外部のアプリケーションからその Web サービスを呼び出し、カスタムのメソッドを実行することが可能になります。次のセクションで、Apex Web サービスを有効化する構文とそのサンプルを紹介します。

Apex から外部の Web サービスを呼び出す Apex コールアウトでは、SOAP と WSDL、または HTTP サービス (RESTful サービス) を利用した Web サービスとの連携をサポートしています。WSDL をインポートして、その WSDL に対応する Apex クラスを自動生成できるほか、HTTP の Request オブジェクトと Response オブジェクトを使用して外部の Web サービスを呼び出す HTTP サービスを利用できます。これらの概念については、 Apex コールアウトセクションで説明します。

なお、Force.com プラットフォームにおける連携オプションの概要については、Integrating with the Force.com Platform (Force.com プラットフォームの統合機能) で解説しています。

リモートサイトの管理とセキュリティ

Apex によって外部サイトを呼び出せるようにするには、Force.com のリモートサイトの設定ページで外部サイトを登録する必要があります。デフォルトでは、未承認のネットワークアドレスに対する呼び出し処理は禁止されています。

設定ページにアクセスするには、[設定]、[セキュリティのコントロール]、[リモートサイトの設定] の順にクリックします。このページでは、登録済みのリモートサイトの名前や URL を確認できます。

RemoteSite.jpg

Force.com プラットフォームでは、セキュリティ上の理由から、設定で使用できる送信用ポートが次の 3 つに制限されています。

  • 80: HTTP 接続のみ受け入れる
  • 443: HTTPS 接続のみ受け入れる
  • 7000 ~ 10000: HTTP 接続と HTTPS 接続を受け入れる

外部の Web サービスを呼び出す場合は、[リモートサイトの設定] でサイトのドメインを必ず追加するようにします。

Apex Web サービス

Apex Web サービスでは、Apex メソッドを記述して、外部のアプリケーションから呼び出せる Web サービスとして公開できます。これにより、Apex コードを公開して、自分が作成したコードや Force.com アプリケーションを外部のアプリケーションから利用可能にすることができます。つまり、Apex によってカスタムの Web サービスを作成できるのです。

今回は、アカウントプランニング用の外部アプリケーションと Force.com 間での連携を可能にする Apex Web サービスを作成するというシナリオを使って説明を行います。ここでは、Apex Web サービスで外部アプリケーションから送信されるアカウントプランを受信し、そのデータを Force.com に同期する必要があります。この後のセクションで、アカウントプランニングをサポートする Apex Web サービスを実現するために必要な Apex クラスの作成手順を解説します。

構文

Apex メソッドをカスタムの Web サービスとして有効化する構文は、非常にシンプルで簡単に作成できます。

1. まず、Apex クラスを作成し、global として定義します。次の例のように、global アクセス修飾子をクラスの定義に追加します。

global class AccountPlan {

}

global アクセス修飾子は、すべての Apex コードがこのクラスに一様にアクセスできることを宣言します。つまり、単一アプリケーション内の Apex コードのみでなく、どの Apex コードもそのクラスを使用できるようになります。


2. 次に、static 修飾子と webservice 修飾子を使用して、Apex メソッドを定義します。

global class AccountPlan {

      webservice static Plan createAccountPlan(Plan vPlan) {
         
               //add logic here......
       }

}

以上により、Force.com プラットフォームが Web サービスを有効化します。Web サービスの詳細を確認するには、自動生成された WSDL 内のサービスに関する記述を参照します。

WSDL にアクセスするには、組織にログイン後、[設定]、[アプリケーションの設定]、[開発]、[Apex クラス] の順にクリックします。Web サービスを定義したクラスを選択し、[WSDL] リンクをクリックすると、WSDL のダウンロードが開始されます。ダウンロードした WSDL を外部のアプリケーションにインポートすると、そのアプリケーションから Web サービスを利用できるようになります。

Web サービスのメンバー変数とクラスの定義

今回のアカウントプランニングのシナリオでは、外部アプリケーションが Apex Web サービス createAccountPlan() を呼び出します。そのため、外部アプリケーションではデータの構造を Force.com スキーマのオブジェクトに変換するのではなく、データを Apex Web サービスにそのまま渡し、Apex Web サービス側でしかるべきロジックにもとづいてデータを適切な Force.com オブジェクトに変換するというやり方が理想的です。

次のコードサンプルには、メンバー変数と内部 Apex クラスを公開して、外部アプリケーションからアクセスできるようにする手順が示されています。webservice 修飾子を使用してメンバー変数と内部 Apex クラスを公開すると、各属性が、該当の Web サービス用の Apex クラスが生成したカスタムの Apex WSDL に追加されます。その後、WSDL をインポートした外部アプリケーションで、他のオブジェクトと同様にこれらのメンバーやクラスを利用できるようになります。

global class AccountPlan {
  
   webservice String area; 
   webservice String region; 
   
   //Define an object in apex that is exposed in apex web service
   global class Plan {
      webservice String name;
      webservice Integer planNumber;
      webservice Date planningPeriod;
      webservice Id planId;
   }

   webservice static Plan createAccountPlan(Plan vPlan) {
       
       //A plan maps to the Account object in salesforce.com. 
       //So need to map the Plan class object to Account standard object
       Account acct = new Account();
       acct.Name = vPlan.name;
       acct.AccountNumber = String.valueOf(vPlan.planNumber);
       insert acct;
       
       vPlan.planId=acct.Id;
       return vPlan;
  }
  

}

Apex によってこのクラス用に自動生成された WSDL には、area と region 用の 2 つの文字列型のメンバーが追加されます。また、内部クラス Plan には webservice 修飾子でアノテーションされたメンバーが含まれているため、WSDL には Plan 用の新規オブジェクトが追加されます。

外部アプリケーションでは、この WSDL をインポートして WSDL 内のオブジェクトをインスタンス化し、必要に応じて Web サービスに渡すことができます。上のコードでは、外部アプリケーションで Plan オブジェクトを作成し、Apex Web サービス createAccountPlan の入力引数に指定することを可能にしています。

重要な考慮事項

webservice キーワードを使用する際には、次の点に注意します。

  • webservice 修飾子は、最上位の外部クラスメソッドと変数、および内部クラスのメンバー変数の定義で使用できます。クラス自体やインターフェース、インターフェースメソッド、インターフェース変数には使用できません。
  • @future キーワードにより非同期として定義されたメソッド内の呼び出し処理は、Apex トリガで実行できます。@future は、Apex メソッドを非同期で実行することを示すアノテーションです。@future アノテーションの詳細については、Force.com Apex コード開発者向けガイド内の関連トピックを参照してください。
  • webservice キーワードを使用して定義したメソッドや変数を含むクラスは、必ず global として宣言します。メソッドや変数、内部クラスを global として宣言した場合は、最上位のクラスも global として宣言する必要があります。
  • webservice キーワードを使用しているメソッドはすべて static として定義する必要があります。
  • 一部の Apex 要素については SOAP に類似の機能が存在しません。そのため、次の要素は、webservice キーワードを使用して定義したメソッドのパラメータとして使用することはできません。これらの要素はメソッド内で使用することは可能ですが、戻り値としては使用できません。
Map
Set
Pattern オブジェクト
Matcher オブジェクト
Exception オブジェクト
  • Web サービスの一部として公開するすべてのメンバー変数では webservice キーワードを使用する必要があります。これらの変数を static として宣言することはできません。詳細については、 こちらのセクションを参照してください。

Apex コールアウト

Apex Web サービスは、外部アプリケーションが Web サービスを介して Apex メソッドを呼び出すことを可能にしますが、それに対して Apex コールアウトは、Apex が外部 Web サービスを呼び出すことを可能にします。これにより、ユーザは Google、Amazon、Facebook といったサードパーティの Web サービスに接続できます。

Apex コールアウトで Web サービスの呼び出しを可能にしている方法は、大きく分けて (a) WSDL ファイルのインポート、(b) HTTP (RESTful) サービスクラスの利用 の 2 つがあります。

WSDL2Apex (WSDL ファイルのインポート機能)

WSDL2Apex を使用すると、WSDL ドキュメントから Apex クラスを自動生成できます。Apex クラスの生成では、SOAP XML の作成、データ転送、Apex オブジェクトに対する応答 XML の解析などが自動的に行われます。こうしたタスクはすべて内部的に処理されるため、Web サービスメッセージを含んだ XML の作成、解析を行うロジックを新たに開発する必要はありません。WSDL2Java を利用したことのある方、.NET の Web 参照として WSDL をインポートする処理をご存じの方には、この機能はなじみがあるものでしょう。

WSDL ファイルを Apex にインポートしてクラスを作成するには、組織にログイン後、[設定]、[開発]、[Apex クラス] の順にメニューを選択し、[WSDL からの生成] をクリックします。

WSDL2Apex.jpg

正しく生成された Apex クラスには、WSDL ドキュメントに定義された、サードパーティの Web サービスを呼び出すためのスタブクラスと型クラスが含まれます。これらのクラスを使用することにより、Apex から外部 Web サービスを呼び出すことが可能になります。

なお、この方法では以下のような点に注意する必要があります。

  • WSDL ドキュメントのサイズの上限は 1 MB です。この上限を超えている場合は、不要なメソッドやデータ型を WSDL から削除してください。
  • 自動生成される Apex クラスに含まれる文字数の上限は 100,000 文字です。この上限を超過していても、Apex コードは生成されますが、コードと共にエラーメッセージが出力され、コードは保存できません。ただし、この場合は、生成された Apex コードをコピーして 2 つ以上の Apex クラスに分割することで対処が可能です。
  • WSDL ドキュメントで Apex の予約語が使われている場合、Apex クラスの生成時に、予約語の末尾に _x が付加されます。

WSDL2Apex のコードサンプル

次に、WSDL2Apex によって生成された Apex メソッドの例を示します。このコードは、Amazon の Web サービスを呼び出します。


  public S3.Status DeleteObject(String Bucket,String Key,String AWSAccessKeyId,DateTime Timestamp,String Signature,String Credential) {
            S3.DeleteObject_element request_x = new S3.DeleteObject_element();
            S3.DeleteObjectResponse_element response_x;
            request_x.Bucket = Bucket;
            request_x.Key = Key;
            request_x.AWSAccessKeyId = AWSAccessKeyId;
            request_x.Timestamp = Timestamp;
            request_x.Signature = Signature;
            request_x.Credential = Credential;
            Map<String, S3.DeleteObjectResponse_element> response_map_x = new Map<String, S3.DeleteObjectResponse_element>();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
              this,
              request_x,
              response_map_x,
              new String[]{endpoint_x,
              '',
              'http://s3.amazonaws.com/doc/2006-03-01/',
              'DeleteObject',
              'http://s3.amazonaws.com/doc/2006-03-01/',
              'DeleteObjectResponse',
              'S3.DeleteObjectResponse_element'}
            );
            response_x = response_map_x.get('response_x');
            return response_x.DeleteObjectResponse;
        }

この Apex メソッドでは、Web サービス呼び出しを実行して、Amazon S3 サービスにアクセスします。結果は Status という Apex クラスに返されます。Web サービスを呼び出す際に、XML の作成や解析が行われていない点に注意してください。こうした処理はすべて Apex 内で内部的に処理されます。具体的には、Web サービスは Apex メソッド WebServiceCallout.invoke を実行するコードによって呼び出されます。通常、一連の処理は自動的に実行されるため、ユーザが Apex クラスを確認する必要はありません。

HTTP ヘッダーのサポート

WSDL2Apex で生成されたコードは HTTP ヘッダーをサポートしているため、認証用のヘッダーに Cookie の値を追加することなどが可能です。HTTP ヘッダーを設定するには、スタブに inputHttpHeaders_x と outputHttpHeaders_x を追加します。

入力用の HTTP ヘッダーを設定するコードサンプルを次に示します。

docSample.DocSamplePort stub = new docSample.DocSamplePort();
stub.inputHttpHeaders_x = new Map<String, String>();

//Setting a basic authentication header
stub.inputHttpHeaders_x.put('Authorization', 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==');

//Setting a cookie header
stub.inputHttpHeaders_x.put('Cookie', 'name=value');

//Setting a custom HTTP header
stub.inputHttpHeaders_x.put('myHeader', 'myValue');
....

出力用の HTTP ヘッダー情報にアクセスするコードサンプルを次に示します。

docSample.DocSamplePort stub = new docSample.DocSamplePort();
stub.outputHttpHeaders_x = new Map<String, String>();
String input = 'This is the input string';
String output = stub.EchoString(input);

//Getting cookie header
String cookie = stub.outputHttpHeaders_x.get('Set-Cookie');

//Getting custom header
String myHeader = stub.outputHttpHeaders_x.get('My-Header');

より詳細な情報やコードサンプルを確認するには、Force.com Apex コード開発者向けガイド内の関連トピックを参照してください。

WSDL2Apex のデバッグ

前述のとおり、WSDL2Apex を使用するメリットは、Web サービスの呼び出しにかかわる面倒な手順をすべて Apex に処理させることができるという点にあります。しかし、開発においては、Apex が生成した SOAP XML メッセージ (要求および応答) の出力の取得をはじめ、Apex メソッドのデバッグが重要となる局面が数多くあります。

このセクションでは、WSDL2Apex の問題を解決する方法を紹介しますが、Force.com IDE の使用経験があることを前提に説明を進めます。

デバッグでは、Force.com IDE の [Execute Anonymous] ビューを使用します。Force.com IDE で [Execute Anonymous] ビューを開き、ダイアログウィンドウにテストケースとする Apex コードを記述します。テストケースには、Web サービスを呼び出す Apex メソッドを実行するために必要なテストデータを必ず含めます。

ExecuteAnon2.jpg

準備が整ったら、[Execute Anonymous] ボタンをクリックします。下部にある [Results] セクションに呼び出し処理の詳細を含む結果が表示されます。 ExecuteAnon1.jpg

このように、Apex による Web サービスの呼び出しを含んだテストケースを作成し、Force.com IDE の [Execute Anonymous] ビューで実行することによって SOAP XML メッセージの出力を取得して、デバッグや問題解決に役立てることができます。

サポートされる WSDL 機能

Apex は、ドキュメントリテラルでラップしたスタイルのみに対応します。サポートされるスキーマタイプは以下のとおりです。

  • String
  • Int
  • Double
  • Float
  • Long
  • Boolean
  • Short
  • Datetime
  • Date
  • anyURI
  • integer
  • language
  • Name
  • NCName
  • NMTOKEN
  • NMTOKENS
  • normalizedString
  • token
  • unsignedLong
  • unsignedShort
  • nonPositiveInteger
  • positiveInteger


サポートされるスキーマ構造は以下のとおりです。

  • Element
  • Sequence
  • All (バージョン 15.0 以降の API で保存された Apex コードの場合)
  • Annotation (バージョン 15.0 以降の API で保存された Apex コードの場合)
  • Attributes (バージョン 15.0 以降の API で保存された Apex コードの場合)
  • Choice (バージョン 15.0 以降の API で保存された Apex コードの場合)
  • Ref


次のデータ型は、外部 Web サービスから Apex の Web サービスメソッドを呼び出す場合にのみサポートされます。Apex の Web サービスメソッドから外部 Web サービスを呼び出す場合にはサポートされません。* blob

  • decimal
  • enum

次のような構造、データ型、サービスはサポートされません。

  • RPC/エンコード形式のサービス
  • portType、service、binding が複数含まれている WSDL ファイル
  • import が含まれている WSDL ファイル
  • 前述のリストに記載のないスキーマタイプ

HTTP (RESTful) サービス

組み込み済みの一部の Apex クラスでは、GET、POST、PUT、DELETE などの HTTP 要求を作成する HTTP サービスをサポートしています。主なクラスは次の 3 つです。

  • HTTP: HTTP 要求、HTTP 応答を実行する場合に使用します。
  • HttpRequest: HTTP 要求をプログラム的に作成する場合に使用します。
  • HttpResponse: HTTP.Send() から返された HTTP 応答を処理する場合に使用します。

これらのクラスにより、Apex で HTTP 要求と HTTP 応答を処理する機能が強化されます。 たとえば、REST ベースのサービスや SOAP ベースの Web サービスとの連携が可能になりますが、SOAP ベースの Web サービスとの連携は、HTTP クラスを使用して要求と応答の双方の SOAP メッセージの作成を柔軟に実行できるという点において WSDL2Apex に代わる手段を実現するものと言えます。

この後のセクションでは、サンプルを示しながら、上記のクラスによって REST ベースの Web サービスを呼び出す方法を説明します。コードの例は、Developerforce の Code Share プロジェクトでも参照できます。たとえば、Force.com Toolkit for Google Data APIs ではこうした呼び出し処理を多数利用しています。

HTTP サービスのコードサンプル

HTTP クラス、HttpRequest クラス、HttpResponse クラスを使用して REST ベースの PUT メッセージを作成し、応答を受信するシンプルなコードの例を次に示します。 ここでは、REST ベースの Web サービスで Amazon S3 サービスを呼び出します。


    HttpRequest req = new HttpRequest(); 
   
    //Set HTTPRequest Method
    req.setMethod('PUT');

    //Set HTTPRequest header properties
    req.setHeader('content-type', 'image/gif');
    req.setHeader('Content-Length','1024');
    req.setHeader('Host','s3.amazonaws.com');
    req.setHeader('Connection','keep-alive');
    req.setEndpoint( this.serviceEndPoint + this.bucket +'/' + this.key);
    req.setHeader('Date',getDateString()); 
 
    //Set the HTTPRequest body	
    req.setBody(body); 	

    Http http = new Http();
    
     try {
	  
          //Execute web service call here		
          HTTPResponse res = http.send(req);	
 
          //Helpful debug messages
          System.debug(res.toString());
          System.debug('STATUS:'+res.getStatus());
          System.debug('STATUS_CODE:'+res.getStatusCode());
				
	 } catch(System.CalloutException e) {
			//Exception handling goes here....
	 }		

上記のコードには、必要な HTTP ヘッダーをすべて設定し、REST ベースのメッセージコンテンツに HttpRequest オブジェクトの内容を指定し、HttpResponse のステータスをチェックして処理が正常に行われたことを確認するという一連の手順が記述されています。

その他の考慮事項

  • Apex コードでは呼び出し処理のタイムアウトを指定できます (タイムアウトの最大値は呼び出し 1 回につき 60 秒)。
次に、WSDL2Apex によって生成した Web サービスの呼び出し処理でカスタムのタイムアウトを指定する例を示します。
docSample.DocSamplePort stub = new docSample.DocSamplePort();
stub.timeout_x = 2000; // timeout in milliseconds
次に、Apex の HttpRequest オブジェクトを使用した HTTP の呼び出し処理でカスタムのタイムアウトを指定する例を示します。
HttpRequest req = new HttpRequest();
req.setTimeout(2000); // timeout in milliseconds
  • 単一の Apex トランザクション内で複数の呼び出し処理を実行する場合のタイムアウトの最大値は 120 秒です。この値は、同一の Apex トランザクションによって実行されたすべての呼び出しの累計に対して適用されます。
  • 呼び出し処理を含んだ Apex クラスのサイズの上限は 100 KBです。
  • Web サービスの呼び出し回数、トリガ当たりの呼び出し回数、Apex クラスの呼び出し回数に対しては、10 回というガバナ制限が適用されます。

まとめ

このドキュメントでは、Apex での Web サービスのサポートについて説明しました。Apex Web サービスと Apex コールアウトは、外部のアプリケーションと Force.com プラットフォームとの連携を可能にする強力な機能です。これらの機能により、Force.com アプリケーションと外部アプリケーションのコラボレーションを容易に実現して、シームレスなユーザエクスペリエンスを提供し、優れたテクニカルアーキテクチャを構築することができるようになります。

参考資料

  • Apex Wiki ページ: Apex コードに関するリソースの包括的なリストを参照できます。
  • Apex Web Service Integration (Apex での Web サービスの連携): Force.com Apex コード開発者向けガイド内の記事。技術文書、構文が公開されています。
  • HTTP クラス: Force.com Apex コード開発者向けガイド内の記事。Apex の HTTP (RESTful) サービスクラスに関する詳細を確認できます。
  • Apex コードのテストメソッドの概要:: Apex のテストメソッドを包括的に紹介しています。Apex Web サービスおよび Apex コールアウトのテストメソッドの記述方法も記載されています。
  • Force.com for Amazon Web Services ツールキット: このページからツールキットをインストールすると、Apex HTTP サービスと WSDL2Apex が生成する Apex コードのサンプルを確認できます。
  • Force.com プラットフォームの統合機能 : Force.com プラットフォームにおける統合のオプションを包括的に紹介しています。

執筆者について

Andrew Albert は、Force.com プラットフォームを中心にセールスフォース・ドットコムテクノロジの普及、啓蒙を担当するテクニカルエバンジェリストです。