Salesforce：Apexから外部サービス呼び出しをする（API）

Salesforce の Apex コードから HTTP 要求 (RESTfulサービス）を送信し、
受信した応答を解析して、Apex にて外部サービスを利用する方法です。


今回はYahoo! 郵便番号検索APIを使った機能を実装していきます。

住所入力があるようなWebフォームをSalesforce（Visualforce＋Apex Controller）で作成します。
郵便番号を入力して検索ボタンをポチってすると、都道府県・市区町村情報がパッと表示される、アレです。:relaxed:
今回は都道府県・市区町村を表示する機能を実装しますが、最寄り駅の情報なども表示できます。便利！
　⇒API：[Webサービス by Yahoo! JAPAN](https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/zipcodesearch.html)	

:::note warn
事前準備
[リモートサイトの設定]にて、アクセス先のサイトのURLを登録する
<font color="red">※セキュリティ上の理由により、Apexから外部サイトにアクセスする場合は登録が必須。登録しないとエラーが発生し、コールアウトが失敗します。

:::

### 前提条件
①以下２つの値をカスタム表示ラベルとして作成しておく。（※URLやAppIdが変わる可能性を考慮）
・Yahoo!へのGETリクエストURL（ZipSearchRequestURL)
・アプリケーションID（ZipSearchAppId）

②表記ゆれを防ぐため郵便番号をハイフンの前後で分けて入力することを想定し、Visualforceにて入力欄を２個用意
（inputZipcode1） -（inputZipCode2）


### 構成　
1.　Webフォームで入力された郵便番号を取得
2.　全角数字を半角数字に変換 （※[全角数字⇒半角数字変換メソッド](https://qiita.com/miku_shinkawa_/items/c3124353f3683f23418b)）
3.　文字種をチェック（半角数字以外ではないことを確認）
4.　郵便番号形式（または桁数）チェック
5.　2~4のチェックで問題がなければ、**HttpRequestを使用し、Yahoo! 郵便番号検索APIへGETリクエストを送信**
6.　**レスポンスを受信し、解析する**　※今回はJSON形式で受け取る
7.　Webフォーム（Visualforce）へ住所をセット


★1～4は外部API関係ないので、構築時の参考程度に。
今回は、主に**５以降の手順**、外部APIの呼び出しの部分のみ書いていく。
<br>


### 実装

```apex:sample.cls
public PageReference SearchZipcode() {
    String reqURL       = System.Label.ZipSearchRequestURL;      //リクエストURL
    String appid        = System.Label.ZipSearchAppId;            //アプリケーションID
    String queryZipCode = inputZipcode1+'-'+inputZipCode2; //クエリ用の郵便番号
    String prefecture = '';   //都道府県格納用変数
    String city       = '';   //市区町村格納用変数
    String oaza      = '';   //大字格納用変数

    /* リクエストを作成 */
    HttpRequest req = new HttpRequest();            
    req.setEndpoint(reqURL + '?query=' + queryZipCode + '&appid=' + appid + +'&output=json&detail=full' );
    req.setMethod('GET');

    /* リクエスト送信 */
    Http http = new Http();
    HTTPResponse res = http.send(req);
            
    /* レスポンス受信 */
    Map<String, Object> resBody = (Map<String, Object>)JSON.deserializeUntyped(res.getBody()); //JSONをMap<String, Object>に並列化
  

    /* Featureがない場合、郵便番号が存在しないためエラー
      （Feature配下に検索結果1件分のデータ群が入っている）*/
    if((List<Object>)resBody.get('Feature') == null){
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, '郵便番号が存在しません。', 'detail - error'));
        return null;
    }
    
    /* Jsonを解析していく */
    List<String> addressList = new List<String>();
    for (Object result : (List<Object>)resBody.get('Feature')) {        
        Map<String, Object> featureMap = (Map<String, Object>)result;
        Map<String, Object> propertyMap = (Map<String, Object>)featureMap.get('Property');
        for(Object address: (List<Object>)propertyMap.get('AddressElement')){
            Map<String, Object> nameMap = (Map<String, Object>)address;
            /* AddressElementの配下のNameに都道府県、市区町村、大字・・・などの階層で情報が格納されている*/
            addressList.add((String)(nameMap.get('Name')));
        }
        /* 上記で格納した順番に取り出し、住所（都道府県から大字まで）を作成していく */
        prefecture = addressList[0];
        city       = addressList[1]
        oaza       = ooazaaddressList[2].substringBefore('（'); 
    }
    /*　（後続処理については省略）　*/
    return null;
}
```


Yahoo!の郵便番号検索は、住所によって、大字の階層に、（大規模ビルを除く）（その他）などの文字が入ってくるので、カッコがあったらそれ以降を切捨てています。


後続処理では、prefecture、city、oazaに入った文字列を画面にセットするだけ:relaxed:



:::note warn
テストクラス作成時の注意

Apexテストメソッドは HTTP コールアウトをサポートしないため、コールアウトを実行するテストは失敗します。
HttpCalloutMock インターフェースを実装するか、
静的リソースを使用してStaticResourceCalloutMock を実装することで
テストクラスが通ります。:ok_woman_tone1:

※やり方はこちらに書きました。⇒[Salesforce：HTTP コールアウトのテストクラス
](https://qiita.com/O__O/items/d6cbd1c0510c0374fa61)
:::


### 最後に
エラー処理は省いているので必要に応じて書いてください。
JSONの解析が大変でした。
どなたかのお役に立てればいいな・・・疲れた・・・( ˘ω˘)ｽﾔｧ