SharePoint REST APIでリストアイテムを操作する。取得 / 登録 / 更新 / 削除。 

## はじめに
SharePointOnlineで作成したリストに対して、SharePoint REST APIを使ってアイテムを操作する方法についてまとめました。

ここではjQueryのAjaxを使用してリクエストを行います。

## 改めて確認・・・SharePoint REST APIって？
Docsにはこのような記載があります。
>SharePoint では、REST を使用して、SharePoint サイトをリモートから操作できるようにする機能が追加されました。 これにより、ユーザーのコードは、標準の REST 機能をサポートする技術を使用することで SharePoint オブジェクトと直接やり取りできます。

要するに、SharePoint REST APIを使うことでSharePointそのものを操作することなく**遠隔から**SharePointアイテムの情報を得たり、追加、更新、削除などができるようになるということ。SharePoint活用の幅が広がりますね。

RESTとかAPIとかの用語については、詳しく載っているサイトがあると思うのでそちらをご覧いただけたらと思います！

## では早速操作してみます。
### 操作対象のリスト
2つのアイテムを格納した「アニメリスト」を作成しました。
<img width="50%" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781744/43d72adf-79ec-f7ed-c2e6-03dd68b009d1.png">
アイテムのプロパティ詳細：

| 内部名 | 列の種類 |
|:-:|:-:|
| Title | 一行テキスト |
| AnimeAuthor | 一行テキスト |

## GET
アイテムを取得するときに使うメソッド
```javascript:getSample.js
  const siteUrl = "https://{組織名}.sharepoint.com/sites/{サイト名}/";

  $.ajax({
    url: `${siteUrl}_api/web/lists/getbytitle('アニメリスト')/items?$select=Title,AnimeAuthor`, // データ取得元のリスト名を表示名で指定
    type: 'GET',
    dataType: 'json',
    headers: {
      "Accept": "application/json;odata=verbose"
    },
  }).done(function (response) {
    // 成功時の処理
  }).fail(function (error) {
    // エラー時の処理
  });
```

### 結果
<img width="50%" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781744/483bd72d-653b-1b7f-2046-b5f3cc2ca511.png">

## POST
アイテムを新規登録するときに使うメソッド
```javascript:postSample.js
  const siteUrl = "https://{組織名}.sharepoint.com/sites/{サイト名}/";

  async function createItem() {
    try {
      // FormDigestValueを取得
      const response = await $.ajax({
        url: `${siteUrl}_api/contextinfo`,
        type: 'POST',
        dataType: 'json',
        headers: {
          "Accept": "application/json;odata=verbose"
        },
      });

      const postHeaders = {
        "Accept": "application/json;odata=verbose",
        "Content-Type": "application/json; odata=verbose",
        "If-Match": "*",
        "X-RequestDigest": response.d.GetContextWebInformation.FormDigestValue // FormDigestValueをセット
      };

      // 登録するアイテムを作成
      const data = JSON.stringify({
        "__metadata": { "type": "SP.Data.AnimeListItem" }, // ListItemEntityTypeFullNameをセット
        'Title': '名探偵コナン',
        'AnimeAuthor': '青山 剛昌',
      });

      // アイテムを登録
      await $.ajax({
        url: `${siteUrl}_api/web/lists/getbytitle('アニメリスト')/items`,
        type: 'POST',
        data: data,
        headers: postHeaders
      });
    } catch (error) {
      // エラー時の処理
    }
  }

// 関数を呼び出す
createItem();
```
### 結果
名探偵コナンが追加されました。
<img width="50%" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781744/6d3780a0-7074-7565-097d-fd9af4803aa6.png">

ポイントは、
- FormDigestValueをヘッダー情報に付与すること。
→ contextinfoエンドポイントにPOSTして取得。
- ListItemEntityTypeFullNameを指定すること。
→ 以下のAPIを叩いて取得するか、
`/_api/web/lists/getbytitle('リスト名')?$select=ListItemEntityTypeFullName`
→ 以下のURLをブラウザに入力しエンターキーで確認することもできる。
`https://{組織名}.sharepoint.com/sites/{サイト名}/_api/lists/getbytitle('リスト名')?$select=ListItemEntityTypeFullName`

また、async/awaitで同期処理的に書いてみました。
リクエストのネストを回避して見通しを良くしたつもりですが、他にも良い書き方があればぜひコメントいただけると嬉しいです。

## MERGE
SharePointに登録されたアイテムを更新するときに使うメソッド
```javascript:mergeSample.js
  const siteUrl = "https://{組織名}.sharepoint.com/sites/{サイト名}/";

  async function margeItem() {
    try {
      // FormDigestValueを取得
      const response = await $.ajax({
        url: `${siteUrl}_api/contextinfo`,
        type: 'POST',
        dataType: 'json',
        headers: {
          "Accept": "application/json;odata=verbose"
        },
      });

      const postHeaders = {
        "Accept": "application/json;odata=verbose",
        "Content-Type": "application/json; odata=verbose",
        "If-Match": "*",
        "X-RequestDigest": response.d.GetContextWebInformation.FormDigestValue // FormDigestValueをセット
      };

      // 更新アイテムのIDを設定
      const id = 4;

      // 更新内容を設定
      const data = JSON.stringify({
        "__metadata": { "type": "SP.Data.AnimeListItem" }, // typeにListItemEntityTypeFullNameの値を指定
        'Title': 'ONE PIECE',
        'AnimeAuthor': '尾田 栄一郎',
      });

      // アイテムを更新
      await $.ajax({
        url: `${siteUrl}_api/web/lists/getbytitle('アニメリスト')/items(${id})`, // 更新対象アイテムのIDをセット
        type: 'MERGE',
        data: data,
        headers: postHeaders,
      });
    } catch (error) {
      // エラー時の処理
    }
  }

  // 関数を呼び出す
  margeItem();
```

### 結果
サザエさんからONE PIECEに更新されました。
<img width="50%" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781744/f6f4e672-ebfc-145e-c839-1c2f38aa156c.png">

## DELETE
アイテムを削除するときに使うメソッド
```javascript:deleteSample.js
  const siteUrl = "https://{組織名}.sharepoint.com/sites/{サイト名}/";

  async function deleteItem() {
    try {
      // FormDigestValueを取得
      const response = await $.ajax({
        url: `${siteUrl}_api/contextinfo`,
        type: 'POST',
        dataType: 'json',
        headers: {
          "Accept": "application/json;odata=verbose"
        },
      });

      const postHeaders = {
        "Accept": "application/json;odata=verbose",
        "Content-Type": "application/json; odata=verbose",
        "If-Match": "*",
        "X-RequestDigest": response.d.GetContextWebInformation.FormDigestValue // FormDigestValueをセット
      };

      // 削除アイテムのIDを設定
      const id = 4;

      // アイテムを削除
      await $.ajax({
        url: `${siteUrl}_api/web/lists/getbytitle('アニメリスト')/items(${id})`, // 削除対象アイテムのIDをセット
        type: 'DELETE',
        headers: postHeaders,
      });
    } catch (error) {
      // エラー時の処理
    }
  }

  // 関数を呼び出す
  deleteItem();
```
### 結果
ONE PIECEが削除されました。
<img width="50%" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781744/96bc8b7b-11f0-cf35-ee57-feb7d8350fac.png">

## これまでの内容にちょっと関係するメモ
- IF-Matchヘッダーについて
  - PUT、MERGE、DELETE 要求を実行するときにアイテムのバージョンを確認するには、If-Match HTTP 要求ヘッダーで ETag を指定する。
  - 指定したETagとアイテムのEtagが異なる場合は、412エラーが返される。
  - バージョンの違いにかかわらず、アイテムを強制的に上書きするには、ETag の値に "*" を設定する。
  - ETag を指定しない場合、SharePointは、バージョンの違いにかかわらずアイテムを上書きする。
  - SharePointでは、SharePointリストとリストアイテムにのみETagが適用される。

- FormDigestValueの取得方法について（以前詰まった所なので、覚書きです。解釈誤りがあったらご教示くださいませ！）
  - _spPageContextInfo変数を使うと、HTTPリクエストなしにFormDigestValueを取得できる。（コンテンツ エディタWebパーツやスクリプトエディタWebパーツでスクリプトを埋め込む）
  - 取得できるのはスクリプトを実行するサイトコレクションのFormDigestValue。スクリプトを実行したサイトコレクションから別のサイトコレクションに対してリクエストしたい場合は認証情報として使えない。

## 参考
https://learn.microsoft.com/ja-jp/sharepoint/dev/sp-add-ins/working-with-lists-and-list-items-with-rest

https://learn.microsoft.com/ja-jp/sharepoint/dev/sp-add-ins/get-to-know-the-sharepoint-rest-service?tabs=http

https://qiita.com/hal220/items/8f83a6a005380ff44e1f

https://marimew.wordpress.com/2015/01/09/sharepoint-rest-knockout-js/

https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/complete-basic-operations-using-sharepoint-rest-endpoints?redirectedfrom=MSDN

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/If-Match