8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Salesforce] カスタムREST APIをApexで作成する

Last updated at Posted at 2021-01-31

Apex REST Web サービス

Apexを使用してカスタムREST APIを公開することができます。

カスタムのREST APIのコーディングについて扱います。
REST APIの定義書についてはこちら
外部システムからのREST APIの呼び出しについてはこちら

REST Web サービスのApexクラスを作成する

  • クラスはglobalにする必要があります。
  • URLはクラス単位で指定します。
  • クラスに@RestResource(urlMapping='/hoge')を追加する必要があります。
    • 指定できるURLはhttps://instance.salesforce.com/services/apexrest/以降になります。
      • 管理パッケージに含まれるリソースの場合はhttps://instance.salesforce.com/services/apexrest/【packageNamespace】/以降になります。
    • URLは大文字、小文字が区別されます。
    • URLパスは/から始める必要があります。
    • ワイルドカード*を使用できます。ワイルドカードの前後には/が必要です(ただし末尾の/は不要)。
    • 常に完全一致が優先されます。完全一致がない場合、ワイルドカードを使用して一致するすべてのパターンを検索し、そのうち文字列の長さが最も長いものが選択されます。

Apex REST メソッドを作成する

  • メソッドはglobal staticにする必要があります。
  • メソッドにアノテーション@HttpGet@HttpPost@HttpPut@HttpPatch@HttpDeleteをつけることで、RESTリソースとしてメソッド(Apex REST メソッド)を公開できます。それぞれ、GET、POST、PUT、PATCH、DELETEのHTTPメソッドに対応します。
  • 一つのクラスでアノテーション@HttpGet@HttpPost@HttpPut@HttpPatch@HttpDeleteを重複して使用することはできません。

リクエストを処理する

リクエストデータの取得

リクエストデータはRestContext.requestで取得できます。

ApexRestメソッド
global static void doPost() {
    RestRequest  req = RestContext.request;
    System.debug(LoggingLevel.DEBUG, '===== request =====');
    System.debug(LoggingLevel.DEBUG, '▼ PATH');
    System.debug(LoggingLevel.DEBUG, req.resourcePath);
    System.debug(LoggingLevel.DEBUG, '▼ URI');
    System.debug(LoggingLevel.DEBUG, req.requestURI);
    System.debug(LoggingLevel.DEBUG, '▼ METHOD');
    System.debug(LoggingLevel.DEBUG, req.httpMethod);
    System.debug(LoggingLevel.DEBUG, '▼ HEADER');
    System.debug(LoggingLevel.DEBUG, req.headers);
    System.debug(LoggingLevel.DEBUG, '▼ PARAM');
    System.debug(LoggingLevel.DEBUG, req.params);

    // ===== request =====
    // ▼ PATH
    // /services/apexrest/test/*
    // ▼ URI
    // /test
    // ▼ METHOD
    // POST
    // ▼ HEADER
    // {Accept=*/*, Accept-Encoding=gzip, deflate, br, ...}
    // ▼ PARAM
    // {p=hoge}
}

リクエストボディの受け取り方

リクエストボディの受け取り方は下記の2つがあります。なお、@HttpGet@HttpDeleteはリクエストボディは使用できません。

  1. 引数への並列化を利用
  2. RestContext.request.requestBodyから取得する

1. 引数への並列化を利用

  • リクエストボディがJSONまたはXMLの場合、Apex REST メソッドに引数を定義すると、引数に並列化されます。
    • レスポンスヘッダにContent-Type: application/jsonまたは、Content-Type: application/xmlが設定されている必要があります。
    • 対応していないContent-Typeを指定するとエラーになります。
      • [{"errorCode": "UNSUPPORTED_MEDIA_TYPE","message": "Content-Type header specified in HTTP request is not supported: text/plain"}]
  • 引数を定義すると、RestContext.request.requestBodyの値はnullになります。
  • 逐次化は下記のオブジェクトが対象となります。
    • Apexプリミティブ型 (SObject、Blobは除く)
    • SObjectまたはApexプリミティブ型のList
    • SObjectまたはApexプリミティブ型のMap(キーはStringのみ可能)
    • 上記の型のメンバ変数を含むユーザ定義型
      • クラスはglobalで宣言されている必要があります。
      • public、private、globalのメンバ変数に並列化します。
      • static、transientの変数には並列化されません。
  • List<List<String>> など、コレクションのList、コレクションのMapはXMLでは使用できません。
  • Apex REST メソッドで定義していないパラメータをリクエストボディに含めるとエラーが返却されます。
    • [{"errorCode": "JSON_PARSER_ERROR","message": "Unexpected parameter encountered during deserialization: productName at [line:1, column:61]"}]
    • [{"errorCode": "XML_PARSER_ERROR","message": "Unexpected parameter encountered during deserialization: productName"}]
  • Apex REST メソッドで定義しているパラメータをリクエストボディに含めなかった場合、エラーにはならず、値はnullになります。
引数への並列化を利用 ①引数にApexプリミティブ型を設定した例
ApexRestメソッド
global static String postConsuming(String productCode, Integer consumedNumber) {
    return productCode + 'の商品を' + consumedNumber + '個消費しました。';
    // "ETA6497-770の商品を5個消費しました。"
}
リクエストボディ(JSON)
{"productCode":"ETA6497-770","consumedNumber":5}
リクエストボディ(XML)
<request>
  <productCode>ETA6497-770</productCode>
  <consumedNumber>5</consumedNumber>
</request>
引数への並列化を利用 ②引数にユーザ定義型を設定した例
ApexRestメソッド
global static String postConsuming(Consuming consumingData) {
    return consumingData.getProductCode() + 'の商品を' + consumingData.getConsumedNumber() + '個消費しました。';
    // "ETA6497-770の商品を5個消費しました。"
}

global class Consuming {
    private String productCode;
    private Integer consumedNumber;

    private String getProductCode() {
        return this.productCode;
    }

    private Integer getConsumedNumber() {
        return this.consumedNumber;
    }
}
リクエストボディ(JSON)
{"consumingData": {"productCode":"ETA6497-770","consumedNumber":5}}
リクエストボディ(XML)
<request>
  <consumingData>
    <productCode>ETA6497-770</productCode>
    <consumedNumber>5</consumedNumber>
  </consumingData>
</request>

2. RestContext.request.requestBodyから取得する

引数を設定しなかった場合、RestContext.request.requestBodyでリクエストボディを取得できます。requestBodyはBlob型です。

ApexRestメソッド
@HttpPatch
global static void insertContact() {
    RestRequest req = RestContext.request;
    // req.requestBody はBlob型なのでStringに変換。
    String reqString = req.requestBody.toString();
    // JSONを並列化
    Map<String, Object> reqJson = JSON.deserializeUntyped(reqString);
    String name = (String) reqJson.get('name');
    String customerNo = (String) reqJson.get('customerNo');
}

レスポンスを返す

レスポンスデータの設定

レスポンスデータはRestContext.responseで設定できます。
設定できる値はステータスコードとヘッダーとボディーの3つです。
ステータスコードに設定可能な値はこちら

ApexRestメソッド
@HttpGet
global static void getProducts() {
    RestResponse res = RestContext.response;
    List<Product2> products = [SELECT ProductCode, Name FROM Product2];

    res.statusCode = 200;
    res.addHeader('Content-Type', 'application/json;charset=UTF-8');
    res.responseBody = Blob.valueOf(JSON.serialize(products));
}

レスポンスボディの設定

レスポンスボディの設定方法は下記の2つがあります。

  1. 戻り値の逐次化を利用
  2. RestContext.response.responseBodyに設定する

1. 戻り値の逐次化を利用

  • Apex REST メソッドの戻り値がvoid以外の場合、戻り値がレスポンスボディに逐次化されます。
  • リクエストヘッダーにAccept:application/xmlが含まれる場合、レスポンスボディがXML形式になります。指定がない場合、もしくはAccept:application/jsonが指定されている場合、レスポンスボディがJSON形式となります。レスポンスヘッダーの値はレスポンスボディの形式に影響しません。
    • 対応していないAcceptの値を指定するとエラーになります。
      • [{"errorCode": "NOT_ACCEPTABLE", "message": "Accept header specified in HTTP request is not supported: text/plain"}]
  • 戻り値には下記の方を使用できます。
    • Apexプリミティブ型 (SObject、Blobは除く)
    • SObjectまたはApexプリミティブ型のList
    • SObjectまたはApexプリミティブ型のMap(キーはStringのみ可能)
    • 上記の型のメンバ変数を含むユーザ定義型
      • クラスはglobalで宣言されている必要があります。
      • public、private、globalのメンバ変数に並列化します。
      • static、transientの変数には並列化されません。
  • RestContext.response.responseBodyではBlob型で設定しますが、戻り値の逐次化を使用する場合、Blob型は使用できません。
  • 戻り値がvoid以外を設定し他状態で、RestContext.response.responseBodyに値を設定しても、エラーにはなりませんが、レスポンスには反映されません。
戻り値の逐次化を利用 ①Apexプリミティブ型を戻り値に設定した例
ApexRestメソッド
@HttpGet
global static String getProducts() {
    return 'test';
}
レスポンスボディ(JSON)
"test"
レスポンスボディ(XML)
<?xml version="1.0" encoding="UTF-8"?>
<response>test</response>
戻り値音逐次化を利用 ②ユーザ定義型を戻り値に設定した例
ApexRestメソッド
@HttpGet
global static ProductItem getProduct() {
    ProductItem item = new ProductItem('ETA6497-770', 'Mainspring');
    return item;
}

global class ProductItem {
    private String code;
    private String name;
    private ProductItem(String pCode, String pName) {
        this.code = pCode;
        this.name = pName;
    }
}
レスポンスボディ(JSON)
{
    "name": "Mainspring",
    "code": "ETA6497-770"
}
レスポンスボディ(XML)
<?xml version="1.0" encoding="UTF-8"?>
<response xmlns:ProductItem="http://soap.sforce.com/schemas/class/RestApiSample">
    <ProductItem:code>ETA6497-770</ProductItem:code>
    <ProductItem:name>Mainspring</ProductItem:name>
</response>

2. RestContext.response.responseBodyに設定する

戻り値にvoidを設定した場合、RestContext.response.responseBodyでレスポンスボディを設定できます。responseBodyはBlob型です。

ApexRestメソッド
@HttpGet
global static void getProduct() {
    RestResponse res = RestContext.response;
    ProductItem item = new ProductItem('ETA6497-770', 'Mainspring');
    res.addHeader('Content-Type', 'application/json');
    res.responseBody = Blob.valueOf(JSON.serialize(item));
}

global class ProductItem {
    private String code;
    private String name;
    private ProductItem(String pCode, String pName) {
        this.code = pCode;
        this.name = pName;
    }
}
レスポンスボディ
{
    "name": "Mainspring",
    "code": "ETA6497-770"
}

参考

8
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?