HTTPメソッドの属性（安全・冪等・Cacheable）

# はじめに

こんにちは。
HTTPの３つ目の記事です。

この記事では、HTTPメソッドの属性について調べていきます。

# メソッドの属性

HTTP Methodは色んな属性を持っています。
その中でも、安全・冪等・Cacheableについて調べていきましょう。

> - **安全 Safe** 
> - **冪等 Idempotent**
> - **キャッシュ可能 Cacheable**


## 安全 Safe

HTTPメソッドの属性。
その１つ目は**安全**についてです。

ここでいう**安全**とは、**リクエストを送信してもリソースは変更されない**という意味です。

例えば、`GET`メソッドは単純にデータを参照する作業を行うため、リソースの変更は起こりません。
つまり、`GET`は**安全**属性を満足するメソッドといえます。

一方、
`POST` `PUT` `DELETE`のようなメソッドでサーバーと通信すると、**リソースの変更または、削除**が起こるため、**安全ではない**メソッドといえます。

もちろん、多数のユーザーがGETでサーバーとの通信を行い、そのリクエストによってサーバーにログがつまり、そのログのせいで障害が発生する恐れがあります。

しかし、**安全**という属性はクライアントがアクセスするリソースの変更及び修正の有無についてのみ、考慮するため、このような危険性は排除されます。

**安全**属性を満足するメソッドは**リソースの変更及び修正が起こらないため、データの一貫性を保つ**ことを言い、このメソッドが全体的な**システムの障害から安全**である意味を持ちます。

## 冪等 Idempotent

HTTPメソッドの属性。
２つ目は**冪等**についてです。

冪等は次のような数式でも表現することができます。

```math
f(f(f(x)))=f(x)
```

**冪等**とは、**同一パラメータで１回リクエストしようが、１００回リクエストしようが、その結果が同じ**であることを言います。

:::note info
ここで、重要なことは**同じリクエストに対する結果が同じ**である場合、そのメソッドを冪等性があると表現できます。
:::

冪等いう概念を安全と混同するかもしれませんが、簡単にその違いを説明すると、
- **安全**：何回リクエストしても、リソースの**変更及び修正が起こらない**こと
- **冪等**：リソースの**変更及び修正が起きても**、複数回リクエストした結果が１回リクエストしたの**結果と同一**であること

### 冪等性を満足するメソッド

まず、**冪等属性を満足しているメソッド**です。
- GET : データを参照するメソッドです
１回リクエストしても、１００回リクエストしても、同じデータが参照されるため、GETメソッドは**安全**と**冪等**属性を両方満足するメソッドです
　
- PUT : PUTはBodyのデータをもとに、リソースを更新するメソッドです
何回リクエストしても、**毎回Bodyのデータでリソースが更新**されるため、**冪等**属性を満足するメソッドです
　
- DELETE : DELETEメソッドはリソースを削除するメソッドです
DELETEメソッドで削除リクエストを１回投げても、１００回なげても、該当の**リソースが削除されていて存在しない結果は同じ**であるため、**冪等**属性を満足するメソッドといえます。

### 冪等性を満足しないメソッド

では、POSTとPATCHはどうでしょうか？

- POSTは冪等属性を満足していません
POSTが決済作業を行っていると想定してみましょう。POSTメソッドが何回もリクエストされると、その分支払いが行われるはずです。
つまり、**リクエストごとの、結果が異なる**ため、冪等ではないメソッドです。

- **PATCH**は設計によって、**冪等属性を満足するケースも、満足しないケースもあります**
例えば、`{ name: "kim"}`をBodyに入れてPATCHでリクエストをすると、単純に既存リソースのnameをkimで変更するだけなので、冪等属性を満足していると言えます。
しかし、`{"operation" : "add", "age" : 10}`のように、リクエストごと、ageに10を足す処理にしたい場合、１回のリクエストと１００回のリクエストの結果が異なります。
この場合は、冪等属性を満足していなくなります。


### 💡冪等の活用

**冪等**という属性は、実際に`HTTPメソッドを設計`するとき、役に立ちます。

冪等は、実際に**自動復旧メカニズムや、サーバーがTIME OUTのような障害**で正常レスポンスを返さないとき、**クライアントが同じリクエストを再送信**してもいいのか？の**判断基準**になります。

例えば、クライアントからDELETEメソッドでリクエストを投げ、リソースを削除するとき、サーバーからレスポンスが返ってこないなら？
冪等属性を満足するメソッドの場合、同じリクエストを投げることができます。

このように冪等属性は上記のようなケースでの重要な判断基準になる属性です。

### ⛔注意点

GETとPUT DELETEが冪等属性を満足しているとはいえ、どんな状況でもいつでも同じ結果を返すという考えはしてはいけません。

次のようなケースを想定してみましょう。

- ユーザー１： GETでAユーザーを参照、結果： {username:A, age:20}
- ユーザー２： PUTでAユーザーを変更 {username:A, age:30}
- ユーザー１： GETでAユーザーを参照、結果： {username:A, age:30}

上記のようにユーザー１がGETでAユーザーを参照します。その後、ユーザー２がAユーザーのageを30に変更します。
ユーザー１がAユーザーをまた参照すれば、ageが30のユーザー情報を受けることになります。

途中で外部によってリソースが変更され、結果が異なる場合もあります。
このような例外ケースも実際には多くあると思います。

しかし、**冪等属性は外部要因による、リソースの変更は全く考慮しません。**

つまり、同じユーザーが同じリクエストをする場合のみを考慮し、冪等属性を満足しているかを判断しましょう。


## キャッシュ可能 Cacheable

最後に説明する属性は**Cacheable**という属性です。

**Cacheable**はサーバーから返ってきた**レスポンスをクライアントがキャッシュで保存し、以降同じリクエストに対して、再利用ができる**ことを言います。

キャッシュは臨時で使えるストレージで、クライアントが１回リクエストを投げて受けたデータに対して、毎回リクエストを投げずに、以前リクエストで受けたデータを保存する場所です。
サイズが大きいIMAGEやJS CSSなどを保存します。

このように**WEBブラウザがリソースを保存できるかで、Cacheableを満足するメソッドを区分**することができます。

HTTPのSPEC上、`GET` `HEAD` `POST` `PATCH`メソッドはキャッシュすることができます。
しかし、実際の業務では`GET`と`HEAD`だけキャッシュとして使われるそうです。

`POST`や`PATCH`は主に**リソースを変更**する作業を行います。
冪等属性がないため、リソースが変更されている恐れがあります。
**キャッシュされているリソースが変更前の古いリソースの場合、間違ったデータをユーザーに提供する危険性**があります。

また、`POST`には重要度が高い**個人情報などが含まれる可能性がある**ため、キャッシュするには**不適切**です。

一方、`GET`メソッドはURLをキーで同一リソースなのかが判断できるため、キャッシュに**適切**なメソッドです。

# 終わりに

HTTPメソッドの属性について調べてきました。

少し難しい内容でしたが、HTTPについてもっと分かった気がします。

冪等性を勉強して、次のような疑問が浮かびました。

> POSTをGETみたいに、データを参照するだけの作業を行うメソッドで定義すれば、POSTは冪等属性を満足していると言えるのではないか？

なので、少しググってみました。

自分と同じ疑問を持っているエンジニアがいて、そのエンジニアがあげた質問に対して、他のエンジニアさんが回答した内容を簡単に説明すると、

冪等性はHTTPメソッドを定まれているHTTP SPECに合わせて設計した場合のみ、保証される属性です。

上記のようにHTTPメソッドの目的にあってない方法では、設計した場合は想定していないみたいです。

そのため、HTTPのAPIを設計するとき、なるべくHTTPメソッドが作られた目的に合わせて設計するのが、服作業を最小限にする方法だと思いました。

# 参考サイト

- https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC
- https://www.inflearn.com/community/questions/110644/patch-%EB%A9%94%EC%84%9C%EB%93%9C%EA%B0%80-%EB%A9%B1%EB%93%B1%EC%9D%B4-%EC%95%84%EB%8B%8C-%EC%9D%B4%EC%9C%A0