ローカルサーバーでflaskのPOSTメソッドが使えない


### やったこと

１、Windows Powershellで`python3 -m http.server 5000`を実行

２、2つ目のWindows Powershellで下記のプログラムを実行

[code]

    from flask import *
    
    app = Flask(__name__)
    
    @app.route("/", methods=["GET", "POST"])
    def odd_even():
            return """
            下に整数を入力してください。奇数か偶数か判定します
            <form action="/" method="POST">
            <input name="num"></input>
            </form>"""
    
    if __name__ == "__main__":
        app.run(debug=True, host='0.0.0.0', port=5000, threaded=True)
    
[/code]

３、3つ目のWindows Powershellで下記のプログラムを実行

[code]

    import requests
    
    url = 'http://localhost:5000/abc'
    data = {'key': 'value'}
    res = requests.post(url, data=data)
    
    print(res.text)
    
[/code]

４、出力結果

[code]

    <!DOCTYPE HTML>
    <html lang="en">
        <head>
            <meta charset="utf-8">
            <title>Error response</title>
        </head>
        <body>
            <h1>Error response</h1>
            <p>Error code: 501</p>
            <p>Message: Unsupported method ('POST').</p>
            <p>Error code explanation: 501 - Server does not support this operation.</p>
        </body>
    </html>
    
[/code]

ポート番号を変える、再インストール、再起動など色々試して調べてなお一向に解決しないので質問させていただきます。使用しているのは普通のデスクトップです。




## 回答の留意事項

  * 便宜上質問文にあるコードを下記のような名前にしています。実際の環境に応じて適宜読み替えてください。 
    * `server.py` : 2で記載しているFlaskアプリのコード。
    * `client.py` : 3で記載しているHTTPリクエストをするコード。
  * 回答時点の質問文にあるコードのみであることを前提としています。

## 基本的な回答

とりあえずで「`server.py`で書いた関数の処理がレスポンスに返ってくることを確認する」点にフォーカスを当てた場合、やろうとしていることの実現をするためには、次のことをする必要があります。

  * 次のいずれかを実施。 
    * `server.py`の`@app.route`で指定しているパスを`/abc`にする。
    * `client.py`でリクエストしようとしているURLを`http://localhost:5000/`にする。
  * **1を実行せずに、** 2の`python server.py`を実行する。

最初のコマンドを実行して待ち受けている時点で、FlaskのWebサーバーは起動できていません。  
（コンソールにはそのような出力がされていると思います）

## 補足等

### http.serverモジュールについて

Ref: <https://docs.python.org/ja/3/library/http.server.html>

`http.server`はPythonの標準ライブラリとして存在する、PythonにてHTTPサーバーの実装をするための機能を提供するモジュールです。  
そして、CLIでの`-m`オプションで指定して利用する場合は、基本的に「GETリクエストのみに対応して静的ファイルをレスポンスする」サーバーとして機能します。

標準的な使い方をする範囲において、この機能の主な役割は「事前に生成したHTML/JS/CSS等の環境を`http://~~`のURLで表示確認をする」までとなっています。  
※例：Sphinx等でドキュメントを生成して、生成先フォルダを`-d`指定してドキュメントHTMLを閲覧する。

つまり、`request.post()`とPOSTリクエストをしようとしている時点で目的と手段が噛み合っていません。（これは4にある出力に`Unsupported
method ('POST').`とあるとおりです）

※一応、コードとコマンドの見直しを行えば`http.server`経由でもFlaskアプリは動くかもしれませんが、現時点では認識外かつ余り意味がないため記載しません。

### Flaskの保持機能としてのHTTPサーバーについて

Flaskはマイクロフレームワークに属するライブラリですが、組み込みでHTTPサーバー機能を保持しています。  
Flaskで想定しているリクエストは受け付けるように出来ているため、ローカルでFlaskのWebアプリを開発しようとする際にはこれを使うのが標準となります。  
※例：`app.route()`で`methods`を指定することで、GETメソッド以外の受付も可能になる。

この`server.py`内でもこれを利用する実装はすでに行われています。  
具体的には`Flask.run()`で、`server.py`内でも`if __name__ ==
"__main__":`のブロック内で実行されるように実装されています。

このブロックは、`python script.py`のように「Pythonの呼び出しスクリプトとして直接ファイルを指定した」ときに実行されます。  
そのため、「基本的な回答」にもあるように`python
server.py`と実行するだけで、ブロック内にある`app.run()`が実行されてFlaskの組み込みWebサーバーを起動することが出来ます。

※実際に、`python -m http.server 5000`と`python
server.py`をそれぞれ別に実行して起動直後のコンソール出力を見比べてみると差異があることが分かるでしょう。

## サーバーとしてのアプリ起動時のポートの取り合いについて

PCがサーバー系アプリケーションを起動する際に待ち受けする場合、原則として **「1つのポートで待ち受けできるのは1つのアプリだけ」** となります。

今回のケースでは`server.py`の`app.run`で5000番ポートで待ち受けようとしています。しかし、`python -m
http.server`の引数でポートを`5000`と明示しており先にこちらが待ち受けてしまっています。

結果として、`client.py`でリクエストしているHTTPサーバーは`server.py`の方ではなく`http.server`の方となってしまい、質問文にあるエラー出力となっています。

## リクエスト時のパス

Flaskは`app.route`で指定したパス以外を受け付けないので、「リクエスト時のURLパス」が「`app.route`で指定するパス」と合致する必要があります。  
そのため、最初の時点ではパスを統一しておかないと動作確認することも出来ません。

### 余談

なお、質問文の最初の組み合わせのとおり「`-m http.server`実行環境下」において「`/abc`というパスのリクエストをする」場合、`python
-m
http.server`実行フォルダ直下の`abc`というファイルを探そうとします。（GETリクエストの場合。今回はPOSTしようとしたため手前で失敗）




## 回答の留意事項

  * 便宜上質問文にあるコードを下記のような名前にしています。実際の環境に応じて適宜読み替えてください。 
    * `server.py` : 2で記載しているFlaskアプリのコード。
    * `client.py` : 3で記載しているHTTPリクエストをするコード。
  * 回答時点の質問文にあるコードのみであることを前提としています。

## 基本的な回答

とりあえずで「`server.py`で書いた関数の処理がレスポンスに返ってくることを確認する」点にフォーカスを当てた場合、やろうとしていることの実現をするためには、次のことをする必要があります。

  * 次のいずれかを実施。 
    * `server.py`の`@app.route`で指定しているパスを`/abc`にする。
    * `client.py`でリクエストしようとしているURLを`http://localhost:5000/`にする。
  * **1を実行せずに、** 2の`python server.py`を実行する。

最初のコマンドを実行して待ち受けている時点で、FlaskのWebサーバーは起動できていません。  
（コンソールにはそのような出力がされていると思います）

## 補足等

### http.serverモジュールについて

Ref: <https://docs.python.org/ja/3/library/http.server.html>

`http.server`はPythonの標準ライブラリとして存在する、PythonにてHTTPサーバーの実装をするための機能を提供するモジュールです。  
そして、CLIでの`-m`オプションで指定して利用する場合は、基本的に「GETリクエストのみに対応して静的ファイルをレスポンスする」サーバーとして機能します。

標準的な使い方をする範囲において、この機能の主な役割は「事前に生成したHTML/JS/CSS等の環境を`http://~~`のURLで表示確認をする」までとなっています。  
※例：Sphinx等でドキュメントを生成して、生成先フォルダを`-d`指定してドキュメントHTMLを閲覧する。

つまり、`request.post()`とPOSTリクエストをしようとしている時点で目的と手段が噛み合っていません。（これは4にある出力に`Unsupported
method ('POST').`とあるとおりです）

※一応、コードとコマンドの見直しを行えば`http.server`経由でもFlaskアプリは動くかもしれませんが、現時点では認識外かつ余り意味がないため記載しません。

### Flaskの保持機能としてのHTTPサーバーについて

Flaskはマイクロフレームワークに属するライブラリですが、組み込みでHTTPサーバー機能を保持しています。  
Flaskで想定しているリクエストは受け付けるように出来ているため、ローカルでFlaskのWebアプリを開発しようとする際にはこれを使うのが標準となります。  
※例：`app.route()`で`methods`を指定することで、GETメソッド以外の受付も可能になる。

この`server.py`内でもこれを利用する実装はすでに行われています。  
具体的には`Flask.run()`で、`server.py`内でも`if __name__ ==
"__main__":`のブロック内で実行されるように実装されています。

このブロックは、`python script.py`のように「Pythonの呼び出しスクリプトとして直接ファイルを指定した」ときに実行されます。  
そのため、「基本的な回答」にもあるように`python
server.py`と実行するだけで、ブロック内にある`app.run()`が実行されてFlaskの組み込みWebサーバーを起動することが出来ます。

※実際に、`python -m http.server 5000`と`python
server.py`をそれぞれ別に実行して起動直後のコンソール出力を見比べてみると差異があることが分かるでしょう。

## サーバーとしてのアプリ起動時のポートの取り合いについて

PCがサーバー系アプリケーションを起動する際に待ち受けする場合、原則として **「1つのポートで待ち受けできるのは1つのアプリだけ」** となります。

今回のケースでは`server.py`の`app.run`で5000番ポートで待ち受けようとしています。しかし、`python -m
http.server`の引数でポートを`5000`と明示しており先にこちらが待ち受けてしまっています。

結果として、`client.py`でリクエストしているHTTPサーバーは`server.py`の方ではなく`http.server`の方となってしまい、質問文にあるエラー出力となっています。

## リクエスト時のパス

Flaskは`app.route`で指定したパス以外を受け付けないので、「リクエスト時のURLパス」が「`app.route`で指定するパス」と合致する必要があります。  
そのため、最初の時点ではパスを統一しておかないと動作確認することも出来ません。

### 余談

なお、質問文の最初の組み合わせのとおり「`-m http.server`実行環境下」において「`/abc`というパスのリクエストをする」場合、`python
-m
http.server`実行フォルダ直下の`abc`というファイルを探そうとします。（GETリクエストの場合。今回はPOSTしようとしたため手前で失敗）

