Blow Up by Black Swan

Python-Flask のレスポンスの仕組み

今回もFlaskについての記事を書きました。テーマはFlaskのレスポンスについてです。Google Cloud FunctionsでFlaskが採用されているため、Flaskのレスポンスに関する知識はFlaskの中でも重要度の高い情報だと思います。他方で、柔軟な設定のために煩雑な麺が否めず、最近人気が出てきたPythonのWebアプリケーションフレームワーク”responder”に劣る部分とも言われます。今回の記事は、コード例と検証結果を交え、わかりやすくなるよう心がけました。この記事が参考になりましたら幸いです。なお、Flaskの他の記事へのリンクを下記に記載していますので、そちらも参考にして頂ければと思います。

1. はじめに(実行環境など)

今回の実行環境は以下になります。また、公式ドキュメントを参考にしていますので、不明点やさらに詳しい内容を知りたい場合はそちらを参照ください。

OS: Mac OSX Python: 3.7.3 Flask: 1.1.1 公式ドキュメントの参考ページ: http://flask.palletsprojects.com/en/1.1.x/quickstart/#about-responses

2. Flaskのレスポンス

Flaskでは、ブラウザなどのクライアントからのリクエストに対するレスポンスで、Responseオブジェクトを生成し対応します。Flaskでレスポンス処理として最も一般的なパターンはrender_templateメソッドを使ってテンプレートを読み込んだデータをview関数の戻り値として返す方法です。しかし、Flaskではこれ以外にもstr型やdict型などが戻り値として指定された場合でも対応するよう設計されています。

view関数からの戻り値の対応パターンは次の5つになります。

  1. Responseオブジェクトの場合
  2. str型データを返す場合
  3. dict型データを返す場合
  4. tuple型データを返す場合
  5. それ以外の場合

以下でそれぞれの対応についてコード例と一緒に説明していきます。なお、コード例については、以下のコードは共通しているものとして省略して記載しています。また実行についても適宜”FLASK_APP”の設定とflask runで実行していますが省略しています。

from flask import Flask, render_template, make_response

app = Flask(__name__)

2-1. Responseオブジェクトを返す場合

まずはview関数の戻り値にResponseオブジェクトが指定された場合です。ResponseオブジェクトはFlaskがもつオブジェクトの1つであり、内部で利用しているWerkzeugパッケージのオブジェクトを継承したものです。このオブジェクトがview関数の戻り値に指定された場合、view関数から直接レスポンスとしてクライアント(ブラウザなど)に返されます。なお、Responseオブジェクトを生成する最も一般的な方法はmake_responseメソッドを利用する方法です。ここでもこのメソッドを利用してResponseオブジェクトを生成しています。

@app.route('/resp1')
def response_object():
    resp = make_response('Response test1: Response Object')
    resp_type = type(resp)
    resp.headers['X-datatype'] = resp_type
    return resp

ブラウザからこのURLにアクセスすると以下の画面が表示されます。

flask-response1

ヘッダーに格納されている”X-datatype”に記載のデータ型からResponseオブジェクトが生成され、ブラウザに返されていることがわかります。

2-2. str型データを返す場合

次は、str型データを返す場合です。view関数の戻り値でstr型データが指定された場合、そのデータとデフォルトパラメータからResponseオブジェクトが生成され、クライアントに返されます。

@app.route('/resp2')
def response_str():
    res = "Response test2: String"
    return res

ブラウザにアクセした時の画面は次のようになります。

flask-response2

2-3. dict型データを返す場合

dict型のデータがview関数の戻り値で指定された場合、Flaskのjsonifyメソッドで自動で処理が行われ、Jsonデータがブラウザに返されます。

@app.route('/resp3')
def response_dict():
    res = {'Response': 'test3', 'data-type': 'dict'}
    return res

これにアクセスした時の取得データを検証します。

>>> import requests
>>> resq = requests.get('http://127.0.0.1:5000/resp3')
>>> resq.json()
{'Response': 'test3', 'data-type': 'dict'}

requestsパッケージのjsonメソッドで解析したことでJsonデータが返されたことがわかります。ブラウザでアクセスすると次のような画面になります。

flask-response3

2-4. tuple型データを返す場合

tuple型の場合は、レスポンスの内容だけでなく、ヘッダー、ステータスコードも指定することができます。タプルの内容は(response, status, headers)(response, status)(response, headers)のどれかになります。statusはステータスコードを表します。またheadersはヘッダーに付加される情報をリストまたはdict型で指定することができます。下記コード例では3つの要素全てを指定しています。

@app.route('/resp4')
def response_tuple():
    resp = "Response test4: tuple"
    status_code = "404"
    headers = {"header1": "one", "header2": "two", "header3": "three"}
    return (resp, status_code, headers)

ここにアクセスするとブラウザでは次のように表示されます。

flask-response4

ステータスコードが404になり、ヘッダーが3つ指定されていることがわかります。

2-5. それ以外

上記以外の場合、Flaskは戻り値を有効なWSGIアプリケーションだと推定し、その値をResponseオブジェクトに変換する試みがFlaskの内部で行われます。ただし、受け付けない値の場合、エラーが発生します。

3. 最後に

以上が、Flaskのレスポンスについてです。基本的にResponseオブジェクトへの変換を行おうとしますが、str型だけでなくタプルなども受け入れるようにできています。またdict型に対してはJsonデータに変換するようにもなっており、API設計にも配慮されています。render_templateメソッドを使うとレスポンスについて特に意識することはありませんが、アプリケーションのセキュリティ対策などでヘッダーに設定値を入れたい場合など、Flaskのレスポンスの仕組みを知っておけばすぐに対応できるようになります。短い記事ではありますが、参考になりましたら幸いです。読んで頂き、ありがとうございました。