Apex RESTでSalesforceに任意のエンドポイントを作る

2021.01.26

SalesforceにはApex RESTという仕組みがあり、任意のApexクラスとメソッドを指定したURIで公開することが可能です。

この記事ではApex RESTを使った簡単なサンプルと作成したエンドポイントの使い方について書きました。

Apex RESTでエンドポイントを作成する

RestResource アノテーションを付与することで、当該クラスをRESTリソースとして公開できます。

REST リソースとして公開するクラスはglobal指定が必要なことに注意してください。

@RestResource(urlMapping='/some_objects/*')
global with sharing class SomeObjectRestResource {
    // Write some REST resources here.
}

RestResourceアノテーションのurlMappingに指定したパスがREST リソースのエンドポイントになります。 上記の例ではurlMapping='/some_objects/*'としているので、エンドポイントはhttps://<instance>.salesforce.com/services/apexrest/some_objects/になります。

また、urlMappingには*(ワイルドカード)を指定していますので、https://<instance>.salesforce.com/services/apexrest/some_objects/<Salesforce Id>などで任意のパラメータを渡すことができます。

Apex RESTのサンプル

Apex REST用に用意されているアノテーションを使って、RESTのGETやPOST、DELETE、PUTを定義できます。

次のサンプルではGETとDELETEを定義しています。

@RestResource(urlMapping='/some_objects/*')
global with sharing class SomeObjectRestResource {
    @HttpGet
    global static SomeObject__c doGet() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;
        String someObjectId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
        SomeObject__c someObject = [SELECT Id, Name, SomeField__c FROM SomeObject__c WHERE Id = :someObjectId];
        return someObject;
    }

    @HttpDelete
    global static void doDelete() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;
        String someObjectId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
        SomeObject__c someObject = [SELECT Id FROM SomeObject__c WHERE Id = :someObjectId];
        delete someObject;
    }
}

カスタムオブジェクト(SomeObject__c)に対して処理を行っていることに注目してください。 この例の様に標準オブジェクトだけでなくカスタムオブジェクトも扱えるので、RESTリソースとして様々な機能を提供できます。

なお、RESTのアノテーションを付与する各メソッドはglobal staticである必要があります。

接続アプリケーションの準備

Apex RESTを利用するためには、Apexの準備の他にSalesforceにて接続アプリケーションを準備する必要があります。

詳しくは、 API インテグレーション用の OAuth 設定の有効化を参照してください。

接続アプリケーションを作成したら、そのコンシューマ鍵とコンシューマの秘密をひかえます。

Apex RESTで定義したエンドポイントを利用する

Apex RESTで定義したエンドポイントを使ってみましょう。 ここではPHPを使った例を示します。

まず、Salesforce APIを実行するクラスSalesforce.php

<?php
/**
 * Salesforce APIを実行するクラス
 */
class Salesforce {
    public static $properties = [];

    function __construct($client_id, $client_secret, $username, $password, $token, $is_product=false) {
        $this->client_id = $client_id;
        $this->client_secret = $client_secret;
        $this->username = $username;
        $this->password = $password;
        $this->token = $token;
        $this->is_product = $is_product;
    }

    /**
     * oauthを取得し返す
     *
     * @return array
     */
    public function oauth() {
        #oauth取得済みの場合、設定されているoauthを返す
        if ( self::property('oauth') ) {
            return self::property('oauth');
        }

        $post = array(
            "grant_type" => "password",
            "client_id" => $this->client_id,
            "client_secret" => $this->client_secret,
            "username" => $this->username,
            "password" => $this->password . $this->token,
        );

        #oauth取得処理
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, 'https://' . ( $this->is_product ? 'login' : 'test' ) . '.salesforce.com/services/oauth2/token');
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt( $curl, CURLOPT_POSTFIELDS, $post );
        $oauth_data = curl_exec($curl);
        $oauth_data = json_decode($oauth_data, true);

        #oauthを設定
        self::property('oauth', $oauth_data);

        return $oauth_data;
    }

    /**
     * 引数で指定したIdを持つSomeObject__c読み出す
     *
     * @param [string] $id
     * @param [string] $rest_resource
     * @return array
     */
    public function read($id, $rest_resource) {
        if ( empty($id) ) return;

        $oauth = self::oauth();

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $oauth['instance_url'] . "/services/apexrest/$rest_resource/$id");
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_HTTPHEADER, [
            'Authorization: '.$oauth['token_type'].' '.$oauth['access_token'],
        ]);

        $data = curl_exec($curl);
        return json_decode($data, true);
    }

    /**
     * 削除処理(指定したIdのSomeObject__cのデータの削除)
     *
     * @param [string] $id
     * @param [string] $rest_resource
     * @return array
     */
    public function delete($id, $rest_resource){
        if( empty($id) ) return;
        $oauth = self::oauth();

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $oauth['instance_url'] . "/services/apexrest/$rest_resource/$id");
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_HTTPHEADER, [
            'Authorization: '.$oauth['token_type'].' '.$oauth['access_token'],
        ]);

        $data = curl_exec($curl);
        return json_decode($data, true);
    }

    /**
     * getter,setter
     *
     * @param [string] $name
     * @param $value
     * @return
     */
    public static function property($name, $value = null) {
        if ( func_num_args() > 1 ) {
            return self::$properties[$name] = $value;
        } else {
            return isset(self::$properties[$name]) ? self::$properties[$name] : null;
        }
    }
}
?>

$oauth['instance_url'] . "/services/apexrest/$rest_resource/$idでエンドポイントのURI(https://<instance>.salesforce.com/services/apexrest/some_objects/<処理対象のSalesforceID>)を構築しています。

次にSalesforceへの接続情報などを記載するconfig.ini

[salesforce]
client_id = '<Salesforce接続アプリケーションのコンシューマ鍵>'
client_secret = '<Salesforce接続アプリケーションのコンシューマの秘密>'
username = '<接続に用いるSalesforceユーザのユーザ名>'
password = '<接続に用いるSalesforceユーザのパスワード>'
token = '<接続に用いるSalesforceユーザのセキュリティトークン>'

[env]
is_production = false

本番環境に接続する場合はis_production = trueにします。

最後に実行ファイルrun.php

<?php
    require_once('./Salesforce.php');

    # 設定ファイルの読み込み
    $config = parse_ini_file("./config.ini", true);

    $id = '<処理対象のSomeObject__cオブジェクトのSalesforceID>';
    $rest_resource = 'some_objects';

    $salesforce = new Salesforce(
        $config['salesforce']['client_id'],
        $config['salesforce']['client_secret'],
        $config['salesforce']['username'],
        $config['salesforce']['password'],
        $config['salesforce']['token'],
        $config['env']['is_production']
    );

    # 読み出し
    $some_objects = $salesforce->read($id, $rest_resource);
    print_r($some_objects);
    # 削除
    $salesforce->delete($some_objects['Id'], $rest_resource);

    exit;
?>

php -f run.php とすることでスクリプトを実行できます。 run.php$idで指定したSomeObject__cのレコードの情報が取得でき、また削除されることを確認できます。 PHPにはSOQLが一つも記載されていないことに注目してください。

まとめ

Apex RESTを使ったRESTエンドポイントの作成方法について書いてみました。 Apex RESTを使うことで、SOQLをApex内に閉じ込めることができ、複雑な処理もApex側に抽象化することができます。 利用する側(run.php)は非常にシンプルに書けるようになるため、選択肢の一つとして知っておくと何かと便利です。

参考資料