Flask は Python で Web 開発をするための素晴らしいマイクロフレームワークで、非常にミニマルなものにすることができます。
数行のコードで、数秒でREST APIを提供することができます。
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello!'
if __name__ == "__main__":
app.run()
現代のWebのバックボーンは、リクエストを送信し、レスポンスを返すHTTPプロトコルです。
これらのリクエストの背後にある意図を区別するために、いくつかの “動詞 “が実行中のタスクに関連付けられました。
GET動詞はリソースを取得したいリクエストの注釈に、
POST動詞はペイロード(ボディ)を指定してリソースの作成をリクエストするために、
DELETE` 動詞はリソースの削除をリクエストするために使用されます、などです。
サーバー上にリソースを作成したい場合 – サーバーに投稿するデータを含むボディを持つ POST リクエストを送信することになります。
となります。
このガイドでは、FlaskでHTTP POST Bodyを取得する方法について説明します。
一般的には、JSON データを REST API に投稿してそのデータを消費させたり、フォームデータを投稿したりすることが多いでしょう – ユーザーが Web フォームに入力し、そのデータを他の API に送信して処理します。
フォームデータを送信する場合、通常は multipart/form-data
としてエンコードされ、JSON データを送信する場合は、通常は application/json
としてエンコードされます。
この情報はPOSTリクエストヘッダに埋め込まれているので、それを確認することもできます。
念のため – データを解析する前に、リクエストのヘッダをチェックします。
リクエストを処理する場合 – flask
の request
モジュールを使用すると、HTTP リクエストを表現することができます。
POST リクエストのボディは、リクエスト自体から直接抽出することができ、エンコーディングに応じて – 適切なフィールドにアクセスすることができます。
-
request.json
またはrequest.get_json()
-) request.form
request.data
request.json
は application/json
という content-type でリクエストされた JSON を表す。
あるいは、 request.get_json()
メソッドを利用することもできます。
フィールドにアクセスしても、メソッドにアクセスしても、受信した JSON に含まれる key-value
のペアを dict
として返します。
注意: json
フィールドと get_json()
メソッドは、POST リクエストの Content-Type が application/json
に設定されているときのみ動作します。
JSON フォーマットの文字列の場合、この方法は失敗し、結果として None
という値が返されます。
もしクライアントが適切にエンコードされたデータを送信するように強制できない場合は、入力された文字列をJSONに変換することができます。
このガイドの後半で説明します。
request.form
は、ウェブフォームから取得した multipart/form-data
データを表します。
request.data
は、入力されたデータを文字列で表現したものである。
一般的には、クライアントが期待するcontent-typeを強制的に送信できない場合に、この表現を使ってJSONに変換することになるでしょう。
POST JSONを取得する
まずはJSONから始めましょう。
これはAPI間でデータを転送するために最もよく使われるフォーマットだからです。
ここでは、/post_json
エンドポイントで POST
リクエストを受け取るシンプルなルートハンドラを作成します。
リクエストのヘッダが application/json
ペイロードとして適切にアノテーションされている場合にのみ、 json
フィールドに値が格納されることを覚えておいてください。
ここでは、ヘッダー
から ‘Content-Type’ を取得し、ボディが本当に application/json
フォーマットであるかどうかを確認します。
もしそうでなければ、リクエストから JSON を抽出することはせず (抽出に失敗します)、エラーメッセージが返されます。
from flask import Flask, request
# ...
@app.route('/post_json', methods=['POST'])
def process_json():
content_type = request.headers.get('Content-Type')
if (content_type == 'application/json'):
json = request.json
return json
else:
return 'Content-Type not supported!'
もし今エンドポイントに POST リクエストを送ると、json が返されます。
$ curl -X POST -H "Content-type: application/json" -d "{"firstName" : "John", "lastName" : "Smith"}" "localhost:5000/post_json"
注意: 使用しているOSやシェルによっては、"
の代わりに'
を使用したり、“のようなエスケープ文字を完全にスキップしたりすることがあります。
この結果、以下のようになります。
{"firstName":"John","lastName":"Smith"}
すごい! では、-H
引数を別の型にして、検証ステップがうまくいくかどうか試してみましょう。
$ curl -X POST -H "Content-type: multipart/form-data" -d "{"firstName" : "John", "lastName" : "Smith"}" "localhost:5000/post_json"
この結果は
Content-Type not supported!
別の方法として、 get_json()
も同じように動作します。
from flask import Flask, request
# ...
@app.route('/post_json', methods=['POST'])
def process_json():
content_type = request.headers.get('Content-Type')
if (content_type == 'application/json'):
json = request.get_json()
return json
else:
return 'Content-Type not supported!'
文字列からPOST JSONを取得する
ここまででリクエストを送信したのは私たちなので、適当に content-type を変更する自由がありました。
時には、JSON 形式のリクエストに正しい content-type が割り当てられていないことがあります。
その場合、 json
や get_json()
は受信した本文を JSON としてパースせず、結局は None
となり、そこから何も取り出すことができません。
このような場合、 json
モジュールを使用して、受け取った文字列を辞書 (key-value
ペア) にロードすることができます。
モジュールをインポートして、受信した request.data
を変換してみましょう。
from flask import Flask, request, json
# ...
@app.route('/post_json', methods=['POST'])
def process_json():
data = json.loads(request.data)
return data
これで、 text/plain
エンコードされたボディを送信しても、 application/json
エンコードされたボディを送信しても、 json
モジュールは入力を処理することができます。
もし、これらのリクエストのどちらかを送信してみると、どちらも同じレスポンスが返ってくるでしょう。
$ curl -X POST -H "Content-type: application/json" -d "{"firstName" : "John", "lastName" : "Smith"}" "localhost:5000/post_json"
$ curl -X POST -H "Content-type: text/plain" -d "{"firstName" : "John", "lastName" : "Smith"}" "localhost:5000/post_json"
これらの結果は
{"firstName":"John","lastName":"Smith"}
POSTフォームの取得
When filling out forms – you have a series of inputs, and their corresponding labels. Under the hood – these are all just key-value
pairs:
username=user_input
password=user_input_2
...
This is typically derived from the front-end – usually an HTML page with a <form>
tag with several <input/>
fields within it. You can also send form data via curl
as:
$ curl -X POST -H "Content-type: multipart/form-data" -F "username=john" -F "password=doe" "localhost:5000/post_form"
Or, you can collapse all of the fields into a single argument:
$ curl -X POST -H "Content-type: multipart/form-data" -F "username=john&password=doe" "localhost:5000/post_form"
In general – you’ll be working with actual forms though, and a form we would be working with looks like:
<form action="/post_form" enctype="multipart/form-data" method="POST">
<input name="username" type="text"/>
<input name="password" type="password"/>
</form>
The name
of each <input/>
is mapped as the key
to a value
input by the user. The form
extracted from the request
object is yet another dict
– and you can access the fields individually easily:
from flask import Flask, request
# ...
@app.route('/post_form', methods=['POST'])
def process_form():
data = request.form
print(data['username'])
print(data['password'])
return data
When we send a request via the console – the dictionary containing the key-value pairs is returned (which is then formatted again, as JSON):
{"password":"doe","username":"john"}
And on the server-end – the inputs for these two fields have been printed, right beneath the log for the incoming request
127.0.0.1 - - [09/Dec/2021 00:24:32] "POST /post_form HTTP/1.1" 200 -
john
doe
Naturally, instead of just printing the values to the console – you would validate the data, create a user, and persist them in the database. あるいは、クラスを使用する前に、この方法でクラスのフィールドにデータを入力することもできます。
結論
このガイドでは、Flask で HTTP POST リクエストを処理する方法について見てきました。
JSON データを受信する方法と、自動的にピックアップされない文字列で表現された JSON を処理する方法についても取り上げました。
最後に、フォームデータについて説明します。
</form