ApexトリガからHttpコールアウト実行とエラーパターン

今回はトリガ+非同期+HTTPコールアウトを組み合わせた処理について、
単発では分かるものも、組み合わせになるとどれが有線されるか曖昧になるので、検証して見ました

スポンサーリンク

トリガ内でのHTTPコールアウト

トリガからHTTPのコールアウトを行う時は、トリガ内から直接コールアウトするのではなく、非同期メソッドの中で実施することが求められます。
コールアウト先URLへのアクセスを、リモートサイトのセキュリティ設定で許可するのも忘れずに。
それぞれ、ケース別に合わせて検証してみました。
ちなみに、この検証で使用しているエンドポイントはデタラメなので、実際に動作確認したい場合実在する接続先を使ってくださいね。

それではケース別に検証

ケース1:トリガから直接httpコール

トリガから直接HTTPコールアウトするように記述しました。

コード

HttpCalloutHogeTrigger.trigger

trigger HttpCalloutHogeTrigger on Account (after insert) {
    if(Trigger.isInsert && Trigger.isAfter){
        for(Account acc : Trigger.New) {
            //HTTPリクエストの作成
            HttpRequest req = new HttpRequest();
            req.setEndpoint('http://hogehoge.com/hogehoge.html');
            req.setMethod('GET');
         
            //HTTPリクエストの送信
            Http http = new Http();
            HttpResponse res = http.send(req);
            
            //レスポンスチェック
            if (res.getStatusCode() == 200) {
                //成功時に実行したい処理
            } else {
                System.debug('Callout failed: ' + res);
            }
        }
    }
}

実行結果

トリガを有効化して、取引先を100件登録すると結果は以下のようになります。

System.CalloutException: Callout from triggers are currently not supported.

→トリガからの直接Httpコールはサポートされていないためエラーになる

ケース2:トリガとクラスを分離して実行(@futureはつけない)

今後はコールアウト部分を別クラスに記載して、トリガからクラスメソッドを呼び出すようにしました。

コード

trigger HttpCalloutHogeTrigger on Account (after insert) {
    if(Trigger.isInsert && Trigger.isAfter){
        for(Account acc : Trigger.New) {
            HogeHttpCallout.callouttest();
        }
    }
}

HogeHttpCallout.cls

public class HogeHttpCallout {

    public static void callouttest() {
        //HTTPリクエストの作成
        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://hogehoge.com/hogehoge.html');
        req.setMethod('GET');
         
        //HTTPリクエストの送信
        Http http = new Http();
        HttpResponse res = http.send(req);
         
        //レスポンスチェック
        if (res.getStatusCode() == 200) {
            //成功時に実行したい処理
        } else {
            System.debug('Callout failed: ' + res);
        }
    }
}

実行結果

System.CalloutException: Callout from triggers are currently not supported.

→処理記述場所がApexクラスに変わっただけで、トリガプロセスの中で実施されている事には変わらないため、ケース1と同じ

ケース3:クラスに@futureアノテーションをつけて実行

今度はApexクラスに@futureアノテーションをつけて非同期で動くようにしました。

コード

HttpCalloutHogeTrigger.trigger

trigger HttpCalloutHogeTrigger on Account (after insert) {
    if(Trigger.isInsert && Trigger.isAfter){
        for(Account acc : Trigger.New) {
            HogeHttpCallout.callouttest();
        }
    }
}

HogeHttpCallout.cls

public class HogeHttpCallout {

    //このメソッドを非同期実行するために、@futureを記述
    @future
    public static void callouttest() {
        //HTTPリクエストの作成
        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://hogehoge.com/hogehoge.html');
        req.setMethod('GET');
         
        //HTTPリクエストの送信
        Http http = new Http();
        HttpResponse res = http.send(req);
         
        //レスポンスチェック
        if (res.getStatusCode() == 200) {
            //成功時に実行したい処理
        } else {
            System.debug('Callout failed: ' + res);
        }
    }
}

実行結果

System.CalloutException: Callout not allowed from this future method. Please enable callout by annotating the future method. eg: @Future(callout=true)

@future(callout=true)を宣言していないので、その状態でコールアウトを行うとエラー

ケース4:@futureアノテーション(callout=true)をつけて、100レコード登録

今度は@futureアノテーションにコールアウトを許可する(callout=true)をつけてみました。
登録件数は100件です。

コード

HttpCalloutHogeTrigger.trigger

trigger HttpCalloutHogeTrigger on Account (after insert) {
    if(Trigger.isInsert && Trigger.isAfter){
        for(Account acc : Trigger.New) {
            HogeHttpCallout.callouttest();
        }
    }
}

HogeHttpCallout.cls

public class HogeHttpCallout {

    //このメソッドを非同期実行するために、@futureを記述
    @future(callout=true)
    public static void callouttest() {
        //HTTPリクエストの作成
        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://hogehoge.com/hogehoge.html');
        req.setMethod('GET');
         
        //HTTPリクエストの送信
        Http http = new Http();
        HttpResponse res = http.send(req);
         
        //レスポンスチェック
        if (res.getStatusCode() == 200) {
            //成功時に実行したい処理
        } else {
            System.debug('Callout failed: ' + res);
        }
    }
}

実行結果

System.LimitException: Too many future calls: 51

→ガバナ制限:Apex呼び出し1回につき許可される futureアノテーションを持つメソッドの最大数の50を超えている

ケース5:50個Insertして実行

コード

コードはケース4と同じです。今後はレコード50件で登録してみます。

実行結果

ガバナ制限に抵触しないので成功

まとめ

以上のように、アノテーションの付け方や呼び出される件数などで、エラー内容が変わることが分かります。
トリガと非同期とコールアウトを組み合わせたケースについのまとめは以下の通りです。

  • トリガプロセス内でHTTPコールアウトはできない
  • ガバナ制限により、一度に呼び出せる非同期メソッドは最大50回
  • 非同期メソッド内でコールアウトする場合は@future(callout=true)宣言が必要

ただ単に正常ケースだけではなくて、エラーが起こった時のエラーメッセージ(例外)についても試験で問われるので覚えておきましょう。

コメント

  1. 朱 偉健 より:

    trigger HttpCalloutHogeTrigger on Account (after insert) {
    if(Trigger.isInsert && Trigger.isAfter){
    for(Account acc : Trigger.New) {
    HogeHttpCallout.callouttest();
    }
    }
    }

    public class HogeHttpCallout {

    //このメソッドを非同期実行するために、@futureを記述
    @future(callout=true)
    public static void callouttest() {
    //HTTPリクエストの作成
    HttpRequest req = new HttpRequest();
    req.setEndpoint(‘http://hogehoge.com/hogehoge.html’);
    req.setMethod(‘GET’);

    //HTTPリクエストの送信
    Http http = new Http();
    HttpResponse res = http.send(req);

    //レスポンスチェック
    if (res.getStatusCode() == 200) {
    //成功時に実行したい処理
    } else {
    System.debug(‘Callout failed: ‘ + res);
    }
    }
    }

    HttpCalloutHogeTriggerの対象レコードのIDをHogeHttpCallout.callouttestにパスして、再度クエリにしたら、もっといいと思いますが、まあ、今回、ここにフォーカスしていない記事だし、余計なことを言ったら、申し訳ございません