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パスは
/
から始める必要があります。 - ワイルドカード
*
を使用できます。ワイルドカードの前後には/
が必要です(ただし末尾の/
は不要)。 - 常に完全一致が優先されます。完全一致がない場合、ワイルドカードを使用して一致するすべてのパターンを検索し、そのうち文字列の長さが最も長いものが選択されます。
- 指定できる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
はリクエストボディは使用できません。
- 引数への並列化を利用
-
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つがあります。
- 戻り値の逐次化を利用
-
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"
}