序章

Webアプリケーションでは、ユーザーからの着信要求データを処理する必要があることがよくあります。 このペイロードは、クエリ文字列、フォームデータ、JSONオブジェクトの形をとることができます。 Flask は、他のWebフレームワークと同様に、リクエストデータにアクセスできます。

このチュートリアルでは、クエリ文字列、フォームデータ、またはJSONオブジェクトのいずれかを受け入れる3つのルートを使用してFlaskアプリケーションを構築します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

  • このプロジェクトでは、Pythonをローカル環境にインストールする必要があります。
  • このプロジェクトでは、 Pipenv を使用します。これは、Pythonの世界にすべてのパッケージングの世界の最高のものをもたらすことを目的とした、本番環境に対応したツールです。 Pipfile、pip、およびvirtualenvを1つのコマンドに利用します。
  • APIエンドポイントをテストするには、Postmanなどのツールをダウンロードしてインストールする必要があります。

このチュートリアルは、Pipenv v2020.11.15、Python v3.9.0、およびFlaskv1.1.2で検証されました。

プロジェクトの設定

リクエストのさまざまな使用方法を示すには、Flaskアプリを作成する必要があります。 サンプルアプリはビュー関数とルートに簡略化された構造を使用していますが、このチュートリアルで学習した内容は、クラスベースのビュー、ブループリント、Flask-Viaなどの拡張機能などのビューを整理するあらゆる方法に適用できます。

まず、プロジェクトディレクトリを作成する必要があります。 ターミナルを開き、次のコマンドを実行します。

  1. mkdir flask_request_example

次に、新しいディレクトリに移動します。

  1. cd flask_request_example

次に、Flaskをインストールします。 ターミナルを開き、次のコマンドを実行します。

  1. pipenv install Flask

pipenvコマンドは、このプロジェクトのvirtualenv、Pipfileを作成し、flask、およびPipfile.lockをインストールします。

プロジェクトのvirtualenvをアクティブ化するには、次のコマンドを実行します。

  1. pipenv shell

Flaskの受信データにアクセスするには、requestオブジェクトを使用する必要があります。 requestオブジェクトは、リクエストからのすべての着信データを保持します。これには、mimetype、リファラー、IPアドレス、生データ、HTTPメソッド、ヘッダーなどが含まれます。

requestオブジェクトが保持するすべての情報は役立つ場合がありますが、この記事では、エンドポイントの呼び出し元から通常直接提供されるデータに焦点を当てます。

Flaskのリクエストオブジェクトにアクセスするには、Flaskライブラリからリクエストオブジェクトをインポートする必要があります。

from flask import request

その後、任意のビュー機能で使用できるようになります。

コードエディタを使用して、app.pyファイルを作成します。 Flaskrequestオブジェクトをインポートします。 また、query-exampleform-example、およびjson-exampleのルートを確立します。

app.py
# import main Flask class and request object
from flask import Flask, request

# create the Flask app
app = Flask(__name__)

@app.route('/query-example')
def query_example():
    return 'Query String Example'

@app.route('/form-example')
def form_example():
    return 'Form Data Example'

@app.route('/json-example')
def json_example():
    return 'JSON Object Example'

if __name__ == '__main__':
    # run app in debug mode on port 5000
    app.run(debug=True, port=5000)

次に、ターミナルを開き、次のコマンドでアプリを起動します。

  1. python app.py

アプリはポート5000で起動するため、次のリンクを使用してブラウザで各ルートを表示できます。

http://127.0.0.1:5000/query-example (or localhost:5000/query-example)
http://127.0.0.1:5000/form-example (or localhost:5000/form-example)
http://127.0.0.1:5000/json-example (or localhost:5000/json-example)

コードは3つのルートを確立し、各ルートにアクセスすると、それぞれ"Query String Example""Form Data Example"、および"JSON Object Example"のメッセージが表示されます。

クエリ引数の使用

クエリ文字列に追加するURL引数は、データをWebアプリに渡す一般的な方法です。 Webを閲覧しているときに、以前にクエリ文字列に遭遇した可能性があります。

クエリ文字列は次のようになります。

example.com?arg1=value1&arg2=value2

クエリ文字列は、疑問符(?)文字の後に始まります。

example.com?arg1=value1&arg2=value2

また、アンパサンド(&)文字で区切られたキーと値のペアがあります。

example.com?arg1=value1&arg2=value2

ペアごとに、キーの後に等号(=)文字が続き、その後に値が続きます。

arg1 : value1
arg2 : value2

クエリ文字列は、ユーザーがアクションを実行する必要のないデータを渡す場合に役立ちます。 アプリのどこかにクエリ文字列を生成してURLに追加すると、ユーザーがリクエストを行うと、データが自動的に渡されます。 クエリ文字列は、メソッドとしてGETを持つフォームによって生成することもできます。

query-exampleルートにクエリ文字列を追加しましょう。 この架空の例では、画面に表示されるプログラミング言語の名前を指定します。 "language"のキーと"Python"の値を作成します。

http://127.0.0.1:5000/query-example?language=Python

アプリを実行してそのURLに移動すると、"Query String Example"のメッセージが引き続き表示されます。

クエリ引数を処理する部分をプログラムする必要があります。 このコードは、request.args.get('language')またはrequest.args['language']のいずれかを使用して、languageキーを読み込みます。

request.args.get('language')を呼び出すことにより、languageキーがURLに存在しない場合でも、アプリケーションは実行を継続します。 その場合、メソッドの結果はNoneになります。

request.args['language']を呼び出すと、languageキーがURLに存在しない場合、アプリは400エラーを返します。

クエリ文字列を処理するときは、アプリが失敗しないようにrequest.args.get()を使用することをお勧めします。

languageキーを読んで、出力として表示してみましょう。

app.pyquery-exampleルートを次のコードで変更します。

app.py
@app.route('/query-example')
def query_example():
    # if key doesn't exist, returns None
    language = request.args.get('language')

    return '''<h1>The language value is: {}</h1>'''.format(language)

次に、アプリを実行して次のURLに移動します。

http://127.0.0.1:5000/query-example?language=Python

ブラウザに次のメッセージが表示されます。

Output
The language value is: Python

URLからの引数は、language変数に割り当てられてから、ブラウザーに返されます。

クエリ文字列パラメータをさらに追加するには、URLの末尾にアンパサンドと新しいキーと値のペアを追加できます。 "framework"のキーと"Flask"の値を作成します。

http://127.0.0.1:5000/query-example?language=Python&framework=Flask

さらに必要な場合は、アンパサンドとキーと値のペアを追加し続けます。 "website"のキーと"DigitalOcean"の値を作成します。

http://127.0.0.1:5000/query-example?language=Python&framework=Flask&website=DigitalOcean

これらの値にアクセスするには、request.args.get()またはrequest.args[]のいずれかを引き続き使用します。 両方を使用して、キーが欠落している場合に何が起こるかを示しましょう。 query_exampleルートを変更して、結果の値を変数に割り当ててから、次のように表示します。

@app.route('/query-example')
def query_example():
    # if key doesn't exist, returns None
    language = request.args.get('language')

    # if key doesn't exist, returns a 400, bad request error
    framework = request.args['framework']

    # if key doesn't exist, returns None
    website = request.args.get('website')

    return '''
              <h1>The language value is: {}</h1>
              <h1>The framework value is: {}</h1>
              <h1>The website value is: {}'''.format(language, framework, website)

次に、アプリを実行して次のURLに移動します。

http://127.0.0.1:5000/query-example?language=Python&framework=Flask&website=DigitalOcean

ブラウザに次のメッセージが表示されます。

Output
The language value is: Python The framework value is: Flask The website value is: DigitalOcean

URLからlanguageキーを削除します。

http://127.0.0.1:5000/query-example?framework=Flask&website=DigitalOcean

languageに値が指定されていない場合、ブラウザはNoneとともに次のメッセージを表示する必要があります。

Output
The language value is: None The framework value is: Flask The website value is: DigitalOcean

URLからframeworkキーを削除します。

http://127.0.0.1:5000/query-example?language=Python&website=DigitalOcean

frameworkの値を想定しているため、ブラウザでエラーが発生するはずです。

Output
werkzeug.exceptions.BadRequestKeyError werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand. KeyError: 'framework'

これで、クエリ文字列の処理について理解できました。 次のタイプの受信データに進みましょう。

フォームデータの使用

フォームデータは、POSTリクエストとしてルートに送信されたフォームから取得されます。 そのため、URLのデータを表示する代わりに(フォームがGETリクエストで送信された場合を除く)、フォームデータはバックグラウンドでアプリに渡されます。 渡されたフォームデータを簡単に確認することはできませんが、アプリはそれを読み取ることができます。

これを示すために、app.pyform-exampleルートを変更して、GET要求とPOST要求の両方を受け入れ、フォームを返します。

app.py
# allow both GET and POST requests
@app.route('/form-example', methods=['GET', 'POST'])
def form_example():
    return '''
              <form method="POST">
                  <div><label>Language: <input type="text" name="language"></label></div>
                  <div><label>Framework: <input type="text" name="framework"></label></div>
                  <input type="submit" value="Submit">
              </form>'''

次に、アプリを実行して次のURLに移動します。

http://127.0.0.1:5000/form-example

ブラウザは、language用とframework用の2つの入力フィールドと送信ボタンを備えたフォームを表示する必要があります。

このフォームについて知っておくべき最も重要なことは、フォームを生成したのと同じルートに対してPOSTリクエストを実行することです。 アプリで読み取られるキーはすべて、フォーム入力のname属性から取得されます。 この場合、languageframeworkが入力の名前であるため、アプリ内の入力にアクセスできます。

ビュー関数内で、リクエストメソッドがGETかPOSTかを確認する必要があります。 GETリクエストの場合は、フォームを表示できます。 それ以外の場合、POSTリクエストの場合は、受信データを処理する必要があります。

app.pyform-exampleルートを次のコードで変更します。

app.py
# allow both GET and POST requests
@app.route('/form-example', methods=['GET', 'POST'])
def form_example():
    # handle the POST request
    if request.method == 'POST':
        language = request.form.get('language')
        framework = request.form.get('framework')
        return '''
                  <h1>The language value is: {}</h1>
                  <h1>The framework value is: {}</h1>'''.format(language, framework)

    # otherwise handle the GET request
    return '''
           <form method="POST">
               <div><label>Language: <input type="text" name="language"></label></div>
               <div><label>Framework: <input type="text" name="framework"></label></div>
               <input type="submit" value="Submit">
           </form>'''

次に、アプリを実行して次のURLに移動します。

http://127.0.0.1:5000/form-example

languageフィールドにPythonの値を入力し、frameworkフィールドにFlaskの値を入力します。 次に、送信を押します。

ブラウザに次のメッセージが表示されます。

Output
The language value is: Python The framework value is: Flask

これで、フォームデータの処理について理解できました。 次のタイプの受信データに進みましょう。

JSONデータの使用

JSONデータは通常、ルートを呼び出すプロセスによって構築されます。

JSONオブジェクトの例は次のようになります。

{
    "language" : "Python",
    "framework" : "Flask",
    "website" : "Scotch",
    "version_info" : {
        "python" : "3.9.0",
        "flask" : "1.1.2"
    },
    "examples" : ["query", "form", "json"],
    "boolean_test" : true
}

この構造により、クエリ文字列やフォームデータとは対照的に、はるかに複雑なデータを渡すことができます。 この例では、ネストされたJSONオブジェクトとアイテムの配列が表示されます。 Flaskはこの形式のデータを処理できます。

app.pyform-exampleルートを変更して、POST要求を受け入れ、GETなどの他の要求を無視します。

app.py
@app.route('/json-example', methods=['POST'])
def json_example():
    return 'JSON Object Example'

クエリ文字列やフォームデータに使用されるWebブラウザーとは異なり、この記事では、JSONオブジェクトを送信するために、Postmanを使用してカスタムリクエストをURLに送信します。

:リクエストのためにPostmanインターフェースをナビゲートするための支援が必要な場合は、公式ドキュメントを参照してください。

Postmanで、URLを追加し、タイプをPOSTに変更します。 本文タブで、 raw に変更し、ドロップダウンからJSONを選択します。

これらの設定は、PostmanがJSONデータを適切に送信できるようにするために必要です。これにより、FlaskアプリはJSONを受信していることを認識します。

POST http://127.0.0.1:5000/json-example
Body
raw JSON

次に、前のJSONの例をテキスト入力にコピーします。

リクエストを送信すると、応答として"JSON Object Example"が返されます。 これはかなり反気候的ですが、JSONデータ応答を処理するためのコードがまだ記述されていないため、予想されます。

データを読み取るには、FlaskがJSONデータをPythonデータ構造に変換する方法を理解する必要があります。

  • オブジェクトであるものはすべてPythondictに変換されます。 JSONの{"key" : "value"}は、Pythonで値を返すsomedict['key']に対応します。
  • JSONの配列は、Pythonのリストに変換されます。 構文は同じなので、リストの例を次に示します。[1,2,3,4,5]
  • JSONオブジェクトの引用符内の値は、Pythonでは文字列になります。
  • ブール値のtrueおよびfalseは、PythonではTrueおよびFalseになります。
  • 最後に、引用符で囲まれていない数字は、Pythonでは数字になります。

次に、着信JSONデータを読み取るためのコードに取り組みましょう。

まず、request.get_json()を使用して、JSONオブジェクトから変数にすべてを割り当てましょう。

request.get_json()はJSONオブジェクトをPythonデータに変換します。 json-exampleルートに次の変更を加えて、着信要求データを変数に割り当て、それらを返します。

app.py
# GET requests will be blocked
@app.route('/json-example', methods=['POST'])
def json_example():
    request_data = request.get_json()

    language = request_data['language']
    framework = request_data['framework']

    # two keys are needed because of the nested object
    python_version = request_data['version_info']['python']

    # an index is needed because of the array
    example = request_data['examples'][0]

    boolean_test = request_data['boolean_test']

    return '''
           The language value is: {}
           The framework value is: {}
           The Python version is: {}
           The item at index 0 in the example list is: {}
           The boolean value is: {}'''.format(language, framework, python_version, example, boolean_test)

トップレベルにない要素にアクセスする方法に注意してください。 ['version']['python']は、ネストされたオブジェクトを入力しているために使用されます。 また、['examples'][0]は、examples配列の0番目のインデックスにアクセスするために使用されます。

リクエストとともに送信されたJSONオブジェクトに、view関数でアクセスされるキーがない場合、リクエストは失敗します。 キーが存在しないときに失敗したくない場合は、キーにアクセスする前に、キーが存在するかどうかを確認する必要があります。

app.py
# GET requests will be blocked
@app.route('/json-example', methods=['POST'])
def json_example():
    request_data = request.get_json()

    language = None
    framework = None
    python_version = None
    example = None
    boolean_test = None

    if request_data:
        if 'language' in request_data:
            language = request_data['language']

        if 'framework' in request_data:
            framework = request_data['framework']

        if 'version_info' in request_data:
            if 'python' in request_data['version_info']:
                python_version = request_data['version_info']['python']

        if 'examples' in request_data:
            if (type(request_data['examples']) == list) and (len(request_data['examples']) > 0):
                example = request_data['examples'][0]

        if 'boolean_test' in request_data:
            boolean_test = request_data['boolean_test']

    return '''
           The language value is: {}
           The framework value is: {}
           The Python version is: {}
           The item at index 0 in the example list is: {}
           The boolean value is: {}'''.format(language, framework, python_version, example, boolean_test)

アプリを実行し、Postmanを使用してサンプルのJSONリクエストを送信します。 応答では、次の出力が得られます。

Output
The language value is: Python The framework value is: Flask The Python version is: 3.9 The item at index 0 in the example list is: query The boolean value is: false

これで、JSONオブジェクトの処理について理解できました。

結論

この記事では、クエリ文字列、フォームデータ、またはJSONオブジェクトのいずれかを受け入れる3つのルートを使用してFlaskアプリケーションを構築しました。

また、すべてのアプローチで、キーが欠落しているときに正常に失敗するという繰り返しの考慮事項に対処する必要があったことを思い出してください。

警告:この記事で取り上げられなかったトピックの1つは、ユーザー入力のサニタイズでした。 ユーザー入力をサニタイズすることで、アプリケーションによって読み取られたデータによって予期しない障害が発生したり、セキュリティ対策が回避されたりすることがなくなります。

Flaskの詳細については、Flaskトピックページで演習とプログラミングプロジェクトを確認してください。