【備忘】フロントエンドとバックエンドをAPIで疎通させようとしたらリクエストがNullになり詰まった

## 最初に実装していたソースコード

### フロントエンド
``` typescript
request_data = {
    key = value,
    key2 = value2
}

const URL = "https://api_endpint/xx/yyy",

const response = await fetch(URL, {
    method: "POST",
    mode: "cors",
    headers: {
        "Content-Type": "application/json",
    },
    body: JSON.stringify(requestData),
});
```

### バックエンド
``` python
"""
中略
"""

@aaa_blueprint.route('/xx/yyy', methods=['OPTION', 'POST'])
def example_route():
    try:
        request_data = request.get_json(silent=True)
        logger.info(f"取得したリクエストデータ: {request_data}")

        return response_example
    except Exception as e:
        logger.exception(f"エラーが発生しました: {e}")
        raise e

```

### Error
``` debug.log
ERROR - 取得したリクエストデータ: None
```


## 原因
### バックエンドでリクエストを受け取る時に「OPTION」を許可しておきながら、OPTIONを適切に処理する処理を実装していなかったから


## 解決策
### クロスアクセスオリジンを許可しない場合。
バックエンドで受け入れるメソッドからOPTIONを削除しよう

``` python
"""
中略
"""

@aaa_blueprint.route('/xx/yyy', methods=['POST'])　#OPTIONを削除
def example_route():
    try:
        request_data = request.get_json(silent=True)
        logger.info(f"取得したリクエストデータ: {request_data}")
        
        """
        ここにビジネスロジック処理
        """

        return response_example
    except Exception as e:
        logger.exception(f"エラーが発生しました: {e}")
        raise e

```


### クロスオリジンを許可する場合
バックエンドでまずOPTIONを受け入れるメソッドを用意して、分離しよう
``` python
"""
中略
"""

@aaa_blueprint.route('/xx/yyy', methods=['POST'])　#OPTIONを削除
def example_route():
    try:
        request_data = request.get_json(silent=True)
        logger.info(f"取得したリクエストデータ: {request_data}")

        """
        ここにビジネスロジック処理
        """

        return response_example
    except Exception as e:
        logger.exception(f"エラーが発生しました: {e}")
        raise e

"""
OPTIONを受け入れる部分のルートを設定
"""
@aaa_blueprint.route('/xx/yyy', methods=['OPTIONS'])
def options_handler():
    response = jsonify()
    response.headers.add("Access-Control-Allow-Origin", "*")
    response.headers.add("Access-Control-Allow-Methods", "POST, OPTIONS")
    response.headers.add("Access-Control-Allow-Headers", "Content-Type")
    return response

```


## 詳細な仕組み

### 注意：調べてみてのまとめになります。初心者のため理解に誤りがあればコメントで指摘をお願いします！

まず、OPTIONメソッドについて

OPTIONSメソッドは、主にCORS（Cross-Origin Resource Sharing）関連で使用されます。CORSは、ブラウザが異なるオリジンからリソースを読み込む際に、リクエストが安全であるかどうかを確認するための仕組みです。

OPTIONSメソッド: CORSプリフライトリクエストに使われることが多いです。ブラウザが異なるオリジン（例えば、フロントエンドがlocalhost:5173、バックエンドがlocalhost:5001）にリクエストを送信する場合、最初にOPTIONSリクエストを送り、サーバーがそのリクエストを許可するかを確認します。サーバーが適切なCORSヘッダーを返すと、実際のリクエスト（例えばPOST）が送信されます。

したがって、OPTIONSメソッドを受け入れることで、CORSプリフライトリクエストに対応し、クロスオリジンのリクエストが正常に処理できるようになります。


次にPOSTメソッドについて
リクエストでPOSTメソッドを使用する場合、ブラウザがCORSのプリフライトリクエスト（OPTIONSメソッド）を送信することがありますが、サーバーがOPTIONSメソッドに適切に応答できるようにしておく必要があります。

もしOPTIONSメソッドをバックエンドで処理しなかった場合、ブラウザがOPTIONSリクエストを送信し、その後にPOSTリクエストが送信される前にエラーが発生します。逆に、POSTメソッドだけを処理する場合、CORSが問題になると、プリフライトリクエスト（OPTIONS）に適切に応答できないため、POSTリクエストがブロックされることがあります。

## つまり
CORSを有効化したい場合、フロントから２回リクエストが送られていたようです。
１回目：CORSが許可されているかな？の確認リクエスト
２回目：本リクエスト（実データが含まれている）

### 上記を元に最初の実装を振り返ってみると？
OPTIONとPOSTを同じエンドポイントでうけたことにより、OPTIONを受け取った状態でビジネスロジック処理に繋げていてしまったので、「は？bodyねえじゃん？エラーです」となっていたわけです。

ということで、ビジネスロジック用のエンドポイントとCORSプリフライトリクエストを受けるエンドポイントを分離しておくことで、ビジネスロジッの処理に入ることができるようになったわけですね。

APIは便利ですが、裏側でどうなっているのかを少しだけ知ることができました。

(公式ドキュメントからは分からなかったので、ログとソースを見比べて、少し修正したらchatGPTに投げて、もう一回修正して・・・の繰り返しで、なんか上手くいったからchatGPTに何をしたかとその理由を教えて欲しいと聞いたら教えてくれただけなのですが・・・)

あくまでも自分の備忘用の記載になるので、お手柔らかに・・・