ODataの基本とSAP内部/外部からの動作確認

この記事は [chillSAP 夏の自由研究2022](https://note.com/chillsap/n/n62740563aee0)、8/7の記事として執筆しています。

# はじめに

この記事では、ODataにあまり馴染みにないSAPコンサルタント向け（私もその一人です）向けに
* ODataの基本
* ODataの探し方
* ODataの基本的な操作方法

について解説します。

# ODataってなんぞや
一言でいうとWebAPIの一種です。

https://docs.microsoft.com/ja-jp/odata/

詳しい定義については、↑のリンク等を参照していただきたいです。

このODataという仕組み、何がすごいかってCRUD全部に対応していることです。
私は以前通常のWebAPIの設計をやったことがあるのですが、実装するのはCRUDのうちどれか一つなんです。
それがODataだとSelectもInsertもUpdateもDeleteも全部できちゃいます。
すごいですよね。

またODataはEntityというデータモデルから構成されていて、これがテーブルを操作するような感覚で使えてしまいます。

何度かODataを触って思いましたが、SAPの標準ないしアドオンのオブジェクト（テーブル）を、構造に値を代入しながら参照・更新できるという意味において、従来のABAPerにとっては標準のBAPIとか汎用モジュールみたいなもんだなと思いました。

# SAP API HUBからODataを探してみる
S/4HANAにかぎらず、あらゆるSAP製品のAPIは下記のページから参照可能です。

https://api.sap.com/

今回はオンプレ版のS/4HANAのODataを参照してみましょう。
トップページから、
Products > S/4HANA > APIsと進みます。
SAPのAPIには、ODataの他にSOAPという形式もあるみたいなのですが、よくわからないのでここでは割愛します。

さて、2022年8月現在、SAPがサポートしているODataにはV2とV4の二種類があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/a6f5e065-f07e-f338-ec5a-8f88c6f6054f.png)

例えば銀行マスタ(BANK)をメンテするODataを検索してみましょう。
V2とV4の両方で見つかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/069d84ee-f337-55b4-c470-0ba617cb07ba.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/2c277c8b-753e-84d5-b831-5b9a6dd63b31.png)

V2とV4で何が違うかよくわかりませんが、今回はBANK ODATA V4 API Version2.0.0の各タブを覗いてみましょう。

## API Reference
このタブでは各種CRUD処理におけるAPIの呼び出し例が記述されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/ee963490-08c4-1f61-60ac-0bfa384ae62c.png)

概ね、
参照：GET
登録：POST
更新：PATCH
だと思っていただければ問題ないと思います。

## Schema View
ここでは、各パラメータの意味や型、必須/任意などを確認できます。
SAPに慣れている人ならわかると思いますが、ほぼほぼ実テーブルの項目定義と適合しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/683d65f5-ea06-7cf9-762b-7d92c9ba3430.png)

↓S/4HANAの銀行マスタのテーブル定義です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/fe71ef0b-0d20-7efc-ee7e-535e333d4434.png)

見ての通りほぼ項目レベルで対応しているのがわかると思います。

# SAPシステムからODataサービスを確認する方法
SAPにどんなODataがあるのか、GUIからTCodeを使って確認できます。
前述の通りSAPのODataにはV2とV4がありますが、それぞれ確認できる口が異なります。

### V2の場合
https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/d4eb54ea54944b04923af573b28a1e7d/34c80bfa05f740bca57c17b941bc28bf.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/2900832b-7a07-6abb-b77d-c5e6b6b5e331.png)

TCODE:/n/IWBEP/REG_SERVICE で検索すると以下の通りHitします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/05b62ce6-71fa-43aa-f57e-4a0705e2eb75.png)


### V4の場合
https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/d4eb54ea54944b04923af573b28a1e7d/3fed8cedb87a457baef1da68edf46bad.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/326b1c0a-8024-c614-33e7-77107ce53b01.png)

TCode:/n/IWFND/V4_ADMIN

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/8e498dc2-cd7c-d17f-81b0-44dc1f309361.png)

# ODataサービスをSAP内部から動作確認する
上記で探したODataをGUIの内部から実際に使ってみることができます。
前述のV4のODataを触ってみましょう。
Service Testを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/e3575c2d-9fbd-31d0-27eb-fecc47ae6097.png)

すると、SAP Gateway clientという画面が立ち上がります。
TCode:/n/IWFND/GW_CLIENT からでもアクセス可能です。

試しに、API HUBにかかれている取得の仕方を試してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/af922131-1f27-fc5b-b705-28f375502c6f.png)


HTTPメソッドはGETにし、↓のRequestURIを入力してExecuteします。

    /sap/opu/odata4/iwbep/all/srvd_a2x/sap/api_bank_2/0001/Bank?$top=10

↓の通り、ちゃんと結果が返ってきますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/6147b2e6-4daf-c1e8-6cdd-71e95a355f64.png)

今度は、POSTメソッドで登録をやってみましょう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/ab0cdd5c-a8c9-1c44-30b0-19817ff4c303.png)

↓RequestURI

    /sap/opu/odata4/iwbep/all/srvd_a2x/sap/api_bank_2/0001/Bank

↓リクエストBody

    {
      "BankCountry": "JP",
      "BankInternalID": "0006004",
      "BankName": "TESTBANK",
      "Region": "27"
    }

HTTPメソッドをPOSTにしてExecuteします。
Statusが201で返ってきているので成功ですね。
(HTTPリクエストのHeaderに、つけてもないのにX-CSRF-Tokenがひっついてきていますが、こちらについてはあとで解説します。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/245adcf0-0547-156c-b2ff-8a6cce67aa31.png)


↓テーブルにもちゃんと登録されてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/bdaefb8b-a0ee-3e39-04de-b20c2100fe57.png)

# ODataサービスをSAP外部から動作確認する
ODataはインターネットに公開されたWebAPIなので、GUIでなくてもHTTPクライアントのソフトからも叩くことができます。

今回私はVSCodeのExtensionであるREST Clientで動作確認してみました。
（POSTMANとかでもいけると思います。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/adc805ec-2334-e8f3-71c4-f8bda26ec5d4.png)

今回はSAP外部からアクセスするので、/sapより右だけでなくRootURLを付与する必要があります。
内部からやったときと同じ要領でGETしてみましょう。

```http:request
https:[RootURL]/sap/opu/odata4/iwbep/all/srvd_a2x/sap/api_bank_2/0001/Bank?$top=10
```

send requestを押下します↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/fac5b469-c5a5-ecf7-a90f-7fa024fffce5.png)

```http:response
HTTP/1.1 401 Unauthorized
content-type: text/html; charset=utf-8
content-length: 6613
sap-system: XXX
www-authenticate: Basic realm="SAP NetWeaver Application Server [XXX/100]"
sap-server: true
```

このように401が返ってきます。
内部から動作確認した時は何も付与する必要がなかったのですが、外部からアクセスするときはBASIC認証をしてあげる必要があります。
SAPのIDPWをリクエストに含める必要があるってことですね。

```http:request
https:[RootURL]/sap/opu/odata4/iwbep/all/srvd_a2x/sap/api_bank_2/0001/Bank?$top=10
Authorization: Basic [SAPのID]:[PW]
```

普通はIDPWをエンコードしたりするものでしょうが、今回は動確なので割愛します。
上記URLとHTTPパラメータでリクエストします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/712d71c5-4c47-2071-7148-af95e64e3c60.png)

ちゃんと結果が返ってきますね。

では今度は登録をやってあげます。
内部で動確したときと同様、BASIC認証するためAuthorizationタグをつけてPOSTしてみましょう。

```http:Request
POST httpshttps:[RootURL]/sap/opu/odata4/iwbep/all/srvd_a2x/sap/api_bank_2/0001/Bank
Authorization: Basic [SAPのID]:[PW]
content-type: application/json

{
  "BankCountry": "JP",
  "BankInternalID": "0006005",
  "BankName": "TESTBANK",
  "Region": "27"
}
```

すると結果は、↓

```http:Response
HTTP/1.1 403 Forbidden
content-type: text/plain; charset=utf-8
content-length: 21
x-csrf-token: Required
sap-server: true
sap-perf-fesrec: XXXX.000000

CSRF token is missing
```

403が返ってきました。
これはCSRFトークンがリクエストに付与されていないためです。
先程内部からPOSTした時はSAP Gateway Clientのほうがかってにトークンをつけてくれたのですが、外部から動確する時はHTTPリクエストにトークンをつけなければなりません。

https://blogs.sap.com/2021/11/04/csrf-token-validation-failed-in-post-method-in-gateway-client/

↑のリンクを参考に、POSTする前にトークンをGETします。（URLは同じでいいです。）

```http:Request
GET https://https:[RootURL]/sap/opu/odata4/iwbep/all/srvd_a2x/sap/api_bank_2/0001/Bank
Authorization: Basic [SAPのID]:[PW]
X-CSRF-Token: Fetch
```

```http:Response
HTTP/1.1 200 OK
content-type: application/json;odata.metadata=minimal;charset=utf-8
content-length: 1498
x-csrf-token: [CSRFトークン]
odata-version: 4.0
```
レスポンスのx-csrf-tokenにトークンが入っているので、返ってきたトークンをPOSTに付与して、もう一度Requestします。

```http:Request
POST https:[RootURL]/sap/opu/odata4/iwbep/all/srvd_a2x/sap/api_bank_2/0001/Bank
Authorization: Basic [SAPのID]:[PW]
content-type: application/json
X-CSRF-Token: [CSRFトークン]
Cookie: 

{
  "BankCountry": "JP",
  "BankInternalID": "0006005",
  "BankName": "TESTBANK",
  "Region": "27"
}
```

```http:Response
HTTP/1.1 201 Created
content-type: application/json;odata.metadata=minimal;charset=utf-8
content-length: 341
location: Bank(BankCountry='JP',BankInternalID='0006005')
odata-version: 4.0
cache-control: no-cache, no-store, must-revalidate
sap-server: true
sap-perf-fesrec: XXXXXXX

{
  "@odata.context": "$metadata#Bank/$entity",
  "@odata.metadataEtag": "W/\"20220727003513\"",
  "BankCountry": "JP",
  "BankInternalID": "0006005",
  "BankName": "TESTBANK",
  "Region": "27",
  "ShortStreetName": "",
  "ShortCityName": "",
  "SWIFTCode": "",
  "BankNetworkGrouping": "",
  "IsMarkedForDeletion": false,
  "Bank": "",
  "BankBranch": "",
  "BankCategory": "",
  "SAP__Messages": []
}
```
201が返ってきましたね。
↓の通り、ちゃんとDBに登録できてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378457/6fcc0424-806b-961a-f554-6dfb5990b190.png)

以上です。

# 参考
https://api.sap.com/

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Authorization

https://qiita.com/toshi0607/items/c4440d3fbfa72eac840c