Flask Request 入門:Webアプリでのリクエスト処理をマスターする
はじめに
Webアプリケーション開発において、クライアント(ブラウザや他のアプリケーション)からの「リクエスト」を受け付け、それに応じた処理を行い、「レスポンス」を返すことは、その根幹をなす動作です。PythonのマイクロフレームワークであるFlaskは、シンプルながらもこのリクエスト・レスポンスサイクルを効率的に処理するための強力なツールを提供します。
この入門記事では、Flaskアプリケーションがどのようにしてクライアントからのリクエストを受け取り、その中に含まれる様々な情報をどのように活用するかに焦点を当てます。具体的には、Flaskが提供するrequest
オブジェクトを中心に、HTTPメソッドの判別、URLパラメータ、フォームデータ、JSONデータ、ファイル、ヘッダー、クッキーといった、リクエストに含まれる多種多様なデータへのアクセス方法を、豊富なコード例と共に詳細に解説します。
この記事を読むことで、あなたはFlaskアプリケーションがクライアントとどのように対話するのかを深く理解し、より複雑でインタラクティブなWebアプリケーションを構築するための基礎知識を身につけることができるでしょう。さあ、Flaskによるリクエスト処理の世界へ踏み込みましょう。
第1章: WebアプリケーションとHTTPリクエストの基礎
Flaskにおけるリクエスト処理を理解するためには、まずWebアプリケーションの基本的な仕組みと、そこで使われるHTTPプロトコルについての知識が必要です。
1.1 Webアプリケーションの基本的な仕組み
Webアプリケーションは、クライアント・サーバーモデルに基づいて動作します。
- クライアント: 主にWebブラウザですが、スマートフォンのアプリや他のサーバーアプリケーションであることもあります。ユーザーからの入力や操作を受け付け、サーバーへリクエストを送信します。
- サーバー: Webアプリケーションのプログラムが動作しているコンピューターです。クライアントからのリクエストを受け付け、それに応じて必要な処理(データベースへのアクセス、計算、ファイルの読み書きなど)を行い、結果をクライアントへレスポンスとして返します。
このクライアントとサーバー間の通信に使われるのが、HTTP (Hypertext Transfer Protocol) というプロトコルです。
1.2 HTTPプロトコルとは?
HTTPは、Web上でデータを交換するためのルールです。クライアントがサーバーに情報を要求したり、サーバーに情報を送信したりする際に使用されます。
HTTPリクエストは、いくつかの部分から成り立っています。
- メソッド (Method): クライアントがサーバーに実行してほしい動作を指定します。主なものにGET, POST, PUT, DELETEなどがあります。
- URL (Uniform Resource Locator): アクセスしたいリソース(Webページ、データ、ファイルなど)の場所を指定します。
- ヘッダー (Headers): リクエストに関する追加情報を含みます。例えば、クライアントの種類(ブラウザの種類やバージョン)、受け付け可能なデータの形式、クッキー情報などがあります。
- ボディ (Body): POSTやPUTなどのリクエストで、サーバーに送信したいデータ本体を含みます。例えば、フォームに入力されたデータや、アップロードされるファイル、JSON形式のデータなどが含まれます。
サーバーは、このHTTPリクエストを受け取り、内容を解析し、適切な処理を行った後にHTTPレスポンスを返します。レスポンスもステータスコード(処理結果を示す番号、例: 200 OK, 404 Not Found, 500 Internal Server Error)、ヘッダー、ボディから構成されます。
1.3 Flaskにおけるリクエストの流れ
Flaskアプリケーションが起動すると、Webサーバー(開発時にはWerkzeugに内蔵されているもの、本番環境ではGunicornやuWSGIなど)がクライアントからのHTTPリクエストを待ち受けます。
リクエストが到着すると、Flaskは以下の処理を行います。
- リクエストの受け取りと解析: Webサーバーは受信した生のHTTPリクエストデータをFlaskに渡します。Flaskはこれを解析し、扱いやすいPythonオブジェクトに変換します。
- ルーティング (Routing): リクエストのURLやメソッドに基づき、どのビュー関数(リクエストを処理するPython関数)を実行すべきかを決定します。これは
@app.route()
デコレーターで定義されたURLルールと照合することで行われます。 - リクエストコンテキストの有効化: 適切なビュー関数が見つかったら、Flaskは「リクエストコンテキスト (Request Context)」を有効にします。これにより、そのリクエストの間、どこからでも現在のリクエスト情報にアクセスできるようになります。
- ビュー関数の実行: マッチしたビュー関数が実行されます。この関数の中で、開発者はリクエストに含まれる様々な情報(パラメータ、フォームデータなど)にアクセスし、それに応じた処理を行います。
- レスポンスの生成: ビュー関数は、処理結果としてレスポンスを返します。これは文字列、HTML、JSON、リダイレクトなどの形式を取り得ます。
- レスポンスの送信: Flaskはビュー関数から返されたレスポンスをHTTPレスポンス形式に変換し、Webサーバーを通じてクライアントに送信します。
- リクエストコンテキストの破棄: リクエストの処理が完了したら、リクエストコンテキストは無効になります。
この一連の流れの中で、特に開発者が関わるのが「ビュー関数の実行」フェーズです。そして、このフェーズでクライアントから送られてきた情報にアクセスするために使用するのが、次に解説するrequest
オブジェクトです。
第2章: Flaskにおけるリクエストオブジェクト (flask.request
)
Flaskでは、現在のリクエストに関するあらゆる情報にアクセスするために、flask.request
という特別なオブジェクトを提供しています。
2.1 request
オブジェクトの正体
flask.request
は、現在のクライアントからのHTTPリクエストを表現するオブジェクトです。このオブジェクトを通じて、リクエストのメソッド、URL、ヘッダー、ボディに含まれるデータなど、様々な情報にアクセスできます。
例えば、以下のコードでは、シンプルにrequest
オブジェクトをインポートして使用しています。
“`python
from flask import Flask, request
app = Flask(name)
@app.route(‘/’)
def index():
# ここで request オブジェクトを使用してリクエスト情報にアクセスできる
user_agent = request.headers.get(‘User-Agent’)
return f’Your User-Agent is: {user_agent}’
if name == ‘main‘:
app.run(debug=True)
“`
2.2 リクエストコンテキスト (Request Context
) について
flask.request
は、Flaskの「コンテキストローカル (Context Local)」という仕組みによって提供されています。これは、同じスレッドやイベントループ内であれば、どの場所からでもグローバル変数のようにアクセスできるように見えるものの、実際には各リクエスト(またはアプリケーション)ごとに固有の値を持つ特殊なオブジェクトです。
なぜこのような仕組みが必要なのでしょうか? Webサーバーは同時に複数のクライアントからのリクエストを処理することがよくあります。もしrequest
が真のグローバル変数だったら、あるリクエストを処理している最中に別のリクエストが来て、request
の値が上書きされてしまい、予期せぬ動作を引き起こす可能性があります。
リクエストコンテキストはこの問題を解決します。Flaskがリクエストを受け取ると、そのリクエスト専用のリクエストコンテキストが作成され、現在の実行環境に関連付けられます。このコンテキストがアクティブな間だけ、flask.request
という名前で現在のリクエストオブジェクトにアクセスできます。別のリクエストが同時に処理されていても、それぞれ独自のリクエストコンテキストを持っているので、flask.request
は常に その リクエストの情報を指します。
ビュー関数が実行されるとき、Flaskは自動的にリクエストコンテキストをプッシュ(有効化)します。ビュー関数の実行が終わると、コンテキストはポップ(無効化)されます。これにより、開発者はリクエストオブジェクトに簡単にアクセスできる一方で、複数リクエストの同時処理における競合を気にする必要がなくなります。
request
オブジェクトは、以下の様々な属性やメソッドを通じて、リクエストの詳細情報を提供します。次に、これらの具体的な使い方を見ていきましょう。
第3章: HTTPメソッドの理解と処理
HTTPリクエストの最も基本的な要素の一つがメソッドです。クライアントがサーバーに対して「何をしてほしいか」を示します。Flaskでは、ルート定義で特定のメソッドのみを受け付けるように設定したり、ビュー関数内でメソッドを判別したりすることができます。
3.1 主要なHTTPメソッド
Webアプリケーションでよく使われるHTTPメソッドには以下があります。
- GET: 特定のリソースを取得するために使用されます。データの変更を伴わない操作(ページの表示、データの検索など)に用いるべきです。リクエストボディは通常含まれません。データはURLのクエリストリングに含まれることがあります。
- POST: サーバーにデータを送信するために使用されます。新しいリソースの作成、フォームデータの送信など、データの変更を伴う操作に用います。データはリクエストボディに含まれます。
- PUT: 特定のURIにあるリソースを、リクエストボディに含まれるデータで完全に置き換えるために使用されます。リソースの更新に用います。
- DELETE: 特定のURIにあるリソースを削除するために使用されます。
- PATCH: リソースの一部のみを更新するために使用されます。
- HEAD: GETと同じですが、サーバーはレスポンスヘッダーのみを返し、ボディは返しません。リソースの存在確認やヘッダー情報の取得に用います。
- OPTIONS: 対象リソースがどのようなHTTPメソッドをサポートしているかを問い合わせるために使用されます。
3.2 Flaskでのメソッド指定
@app.route()
デコレーターを使う際、methods
引数に許可するHTTPメソッドのリストを指定できます。デフォルトは['GET']
です。
“`python
from flask import Flask
app = Flask(name)
GETメソッドのみ許可(デフォルトなので指定は必須ではないが、明示すると分かりやすい)
@app.route(‘/data’, methods=[‘GET’])
def get_data():
return ‘This is data from GET request.’
POSTメソッドのみ許可
@app.route(‘/data’, methods=[‘POST’])
def create_data():
# POSTデータの処理は後述
return ‘Data received via POST request.’
GETとPOSTの両方を許可
@app.route(‘/submit’, methods=[‘GET’, ‘POST’])
def submit_form():
if request.method == ‘POST’:
# POSTデータ処理
return ‘Form submitted via POST.’
else: # request.method == ‘GET’
# フォーム表示など
return ‘Please submit the form.’
if name == ‘main‘:
app.run(debug=True)
“`
特定のメソッドが許可されていないルートにそのメソッドでアクセスした場合、Flaskは自動的に405 Method Not Allowedエラーレスポンスを返します。
3.3 request.method
による判別
複数のメソッドを許可するルートでは、ビュー関数内でrequest.method
属性を使って、実際のリクエストがどのメソッドで送信されたかを判別し、処理を切り替えるのが一般的です。
“`python
from flask import Flask, request, render_template_string
app = Flask(name)
HTML_FORM = “””
Submit Form
“””
@app.route(‘/submit’, methods=[‘GET’, ‘POST’])
def submit_form():
if request.method == ‘POST’:
# POSTリクエストの場合、フォームデータを処理
name = request.form.get(‘name’, ‘Anonymous’) # request.form は後述
message = request.form.get(‘message’, ‘No message’)
return f’Thank you, {name}! Your message “{message}” has been received.’
else: # GETリクエストの場合、フォームを表示
return render_template_string(HTML_FORM)
if name == ‘main‘:
app.run(debug=True)
“`
この例では、/submit
へのGETリクエストではHTMLフォームを表示し、同じURLへのPOSTリクエストでは送信されたフォームデータを処理しています。
第4章: URLパラメータとクエリストリングの取得
リクエストからデータを受け取る一般的な方法の一つに、URL自体やその末尾に付加されるクエリストリングを利用する方法があります。
4.1 URLルール内の変数 (<variable>
)
Flaskのルーティングでは、URLパスの一部を変数としてキャプチャできます。これは、特定のリソース(例: ユーザーID、記事ID)を指定する際によく使われます。
@app.route()
デコレーターで<variable_name>
のように指定すると、その部分の値がビュー関数のキーワード引数として渡されます。型を指定することもできます(例: <int:user_id>
, <string:username>
, <path:filepath>
)。
“`python
from flask import Flask
app = Flask(name)
@app.route(‘/user/
def show_user_profile(username):
# username は URL から取得される文字列
return f’User: {username}’
@app.route(‘/post/
def show_post(post_id):
# post_id は URL から取得される整数
return f’Post ID: {post_id} (Type: {type(post_id)})’
パス区切り文字を含むファイルパスを取得
@app.route(‘/files/
def show_filepath(filepath):
# filepath は URL から取得されるパス文字列
return f’File Path: {filepath}’
if name == ‘main‘:
app.run(debug=True)
“`
/user/alice
にアクセスすると “User: alice”、/post/123
にアクセスすると “Post ID: 123 (Type: /files/usr/local/data.txt
にアクセスすると “File Path: usr/local/data.txt” と表示されます。
4.2 クエリストリング (?key=value&...
)
URLのパス部分の後に?
が続き、key1=value1&key2=value2...
という形式で付加されるのがクエリストリングです。これは主にGETリクエストで、フィルタリング条件、検索キーワード、ページ番号などの追加情報をサーバーに渡すために使用されます。
例えば、/search?q=flask&page=1
のようなURLでは、q
とpage
がクエリストリングのキーです。
4.3 request.args
の使い方
Flaskでは、リクエストのクエリストリングはrequest.args
というオブジェクトを通じてアクセスできます。これはImmutableMultiDictという辞書ライクなオブジェクトで、クエリストリングのキーと値を保持しています。
request.args
から値を取得するには、通常の辞書のようにrequest.args['key']
とアクセスすることも可能ですが、指定したキーが存在しない場合にKeyErrorが発生します。安全にアクセスするためには、辞書のget()
メソッドを使用するのが一般的です。get()
メソッドは、キーが存在しない場合にNone
(または指定したデフォルト値)を返すため、エラーを防ぐことができます。
“`python
from flask import Flask, request
app = Flask(name)
@app.route(‘/search’)
def search():
# クエリストリング ?q=…&page=… を取得
query = request.args.get(‘q’) # キー ‘q’ の値を取得。なければ None
page = request.args.get(‘page’, ‘1’) # キー ‘page’ の値を取得。なければ ‘1’ をデフォルト値とする
if query:
return f'Searching for: "{query}" on page {page}'
else:
return 'Please provide a search query using the "q" parameter.'
if name == ‘main‘:
app.run(debug=True)
“`
/search?q=python
にアクセスすると: “Searching for: “python” on page 1″/search?q=flask&page=2
にアクセスすると: “Searching for: “flask” on page 2″/search
にアクセスすると: “Please provide a search query using the “q” parameter.”
request.args
はImmutableMultiDictであるため、同じキーが複数回出現する場合(例: /items?tag=python&tag=web
)、get()
メソッドは最初に出現した値のみを返します。全ての値のリストを取得したい場合は、request.args.getlist('key')
メソッドを使用します。
“`python
from flask import Flask, request
app = Flask(name)
@app.route(‘/items’)
def list_items():
# 例: /items?tag=python&tag=web&tag=framework
tags = request.args.getlist(‘tag’)
if tags:
return f’Filtering items by tags: {“, “.join(tags)}’
else:
return ‘Showing all items.’
if name == ‘main‘:
app.run(debug=True)
“`
/items?tag=python&tag=web
にアクセスすると “Filtering items by tags: python, web” と表示されます。
第5章: フォームデータの処理 (POSTリクエスト)
Webアプリケーションでユーザーからの入力を受け付ける最も一般的な方法の一つが、HTMLフォームを使用することです。ユーザーがフォームに入力し、「送信」ボタンをクリックすると、通常はPOSTリクエストとしてそのデータがサーバーに送信されます。
5.1 HTMLフォームの設定
フォームデータをPOSTリクエストとして送信するには、HTMLの<form>
タグでmethod="post"
を指定し、各入力要素(<input>
, <textarea>
, <select>
など)にname
属性を付けます。name
属性の値が、サーバーに送信されるデータのキーとなります。
“`html
“`
このフォームが送信されると、リクエストボディにはuser_name=...&user_email=...&user_message=...
のような形式(application/x-www-form-urlencoded
エンコーディングの場合)または、より複雑なマルチパート形式でデータが含まれます。
5.2 request.form
の使い方
Flaskでは、POSTリクエストのボディに含まれるフォームデータはrequest.form
というオブジェクトを通じてアクセスできます。request.args
と同様に、これもImmutableMultiDictという辞書ライクなオブジェクトです。
クエリストリングと同様に、request.form
から値を取得する際も、get()
メソッドを使って安全にアクセスするのがベストプラクティスです。
“`python
from flask import Flask, request, render_template_string
app = Flask(name)
HTML_FORM = “””
Contact Us
“””
@app.route(‘/contact’, methods=[‘GET’, ‘POST’])
def contact():
if request.method == ‘POST’:
# request.form からフォームデータを取得
name = request.form.get(‘name’)
email = request.form.get(‘email’)
message = request.form.get(‘message’)
# ここで取得したデータを使って何らかの処理を行う(例: メール送信、DB保存)
if name and email and message:
return f'Thank you, {name}! Your message has been received.<br>Email: {email}<br>Message: {message}'
else:
return 'Error: Please fill out all fields.'
else: # GETリクエストの場合はフォームを表示
return render_template_string(HTML_FORM)
if name == ‘main‘:
app.run(debug=True)
“`
/contact
にGETでアクセスするとフォームが表示されます。- フォームに入力して送信すると、POSTリクエストが
/contact
に送られ、入力した内容が表示されます。
request.form.get('key')
は、そのキーに対応する値が存在しない場合はNone
を返します。また、request.form.get('key', default_value)
のようにデフォルト値を指定することもできます。
request.form
もImmutableMultiDictなので、同じ名前の入力フィールドが複数ある場合(例: 複数のチェックボックスが同じname
を持つ場合)、request.form.getlist('name')
を使って値のリストとして取得する必要があります。
第6章: JSONデータの処理 (APIエンドポイントなど)
RESTful APIなどを構築する際には、クライアントからJSON形式でデータを受け取ることが非常に一般的です。これは、Webブラウザだけでなく、他のサーバー、モバイルアプリ、JavaScriptの非同期通信(Ajax/Fetch API)など、様々なクライアントからのリクエストで利用されます。
6.1 JSON形式のデータの送り方
クライアントは、リクエストボディにJSON文字列を含め、ヘッダーにContent-Type: application/json
を指定してリクエストを送信します。
例 (JavaScriptのFetch API):
javascript
fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: 'Alice', age: 30 })
})
.then(response => response.json())
.then(data => console.log(data));
6.2 request.json
または request.get_json()
の使い方
Flaskでは、Content-Type
ヘッダーがapplication/json
であるか、またはそのリクエストがJSONボディを含んでいると判断される場合、リクエストボディのJSONデータを自動的にパース(解析)し、Pythonの辞書やリストに変換してくれます。このパースされたデータには、request.json
またはrequest.get_json()
を通じてアクセスできます。
request.json
: リクエストがJSONを含まない場合、またはJSONのパースに失敗した場合にNone
になります。request.get_json()
: より柔軟なオプションを提供します。force=True
:Content-Type
ヘッダーに関わらず、強制的にJSONとして扱おうとします。silent=True
: JSONのパースに失敗してもエラー(400 Bad Request)を発生させず、None
を返します。デフォルトではパースエラー時に400エラーが発生します。cache=False
: デフォルトでは一度パースしたJSONはキャッシュされますが、これを無効にします。通常はデフォルトで問題ありません。
JSONデータの取得には、パース失敗時のエラーハンドリングが重要となるため、request.get_json(silent=True)
を使うか、またはrequest.json
を使う場合はNoneチェックを行うのが良いでしょう。
“`python
from flask import Flask, request, jsonify
app = Flask(name)
@app.route(‘/api/data’, methods=[‘POST’])
def process_json_data():
# JSONデータを取得
# silent=True をつけると、JSONが無効でもエラーにならず None が返る
data = request.get_json(silent=True)
if data is None:
# JSONデータがないか、形式が不正
return jsonify({"error": "Invalid or missing JSON data"}), 400
# JSONデータが取得できた場合
name = data.get('name') # 辞書のようにアクセス
age = data.get('age')
if name and age is not None:
return jsonify({
"message": f"Data received: Name is {name}, Age is {age}",
"status": "success"
}), 200
else:
# 必須フィールドが不足している
return jsonify({"error": "Missing required fields (name or age)"}), 400
if name == ‘main‘:
app.run(debug=True)
“`
この例では、/api/data
へのPOSTリクエストに対して、JSONボディを期待しています。request.get_json(silent=True)
でデータを取得し、取得できたか(Noneでないか)、そして必要なキー(name
とage
)が存在するかをチェックしています。不足があれば適切なエラーレスポンス(ステータスコード400)を返しています。
第7章: ファイルアップロードの処理
ユーザーが画像やドキュメントなどのファイルをサーバーにアップロードできるようにしたい場合、Flaskではこれを簡単に処理できます。
7.1 HTMLフォームの設定(ファイルアップロード用)
ファイルアップロードを含むフォームを送信するには、HTMLの<form>
タグで以下の2つの属性を適切に設定する必要があります。
method="post"
: ファイルデータはリクエストボディで送信されるため、POSTメソッドを使います。enctype="multipart/form-data"
: ファイルを含むフォームデータを正しくエンコードするためのエンコーディングタイプを指定します。
そして、ファイル選択用の入力要素として<input type="file" name="file_field_name">
を使用します。name
属性の値が、サーバー側でファイルにアクセスするためのキーになります。
“`html
“`
7.2 request.files
の使い方
Flaskでは、ファイルアップロードを含むPOSTリクエストにおいて、アップロードされたファイルはrequest.files
というオブジェクトを通じてアクセスできます。これもまたImmutableMultiDictです。
request.files
のキーは、HTMLフォームの<input type="file">
要素のname
属性の値です。ここから取得できる値は、werkzeug.datastructures.FileStorage
という型のオブジェクトです。
FileStorage
オブジェクトは以下の便利な属性とメソッドを持っています。
filename
: クライアントから送信されたオリジナルのファイル名。ただし、この値をそのままファイルシステムに保存するのはセキュリティ上危険です(パストラバーサル攻撃など)。常にファイル名をサニタイズまたは変更して保存する必要があります。mimetype
: ファイルのMIMEタイプ(例:image/png
,text/plain
)。stream
: ファイルの内容を読み書きできるストリーム(ファイルオブジェクト)。read()
: ファイルの内容全体をバイト列として読み込みます。save(destination)
: ファイルを指定したパス(ファイル名を含む)に保存します。
ファイルを取得する際も、request.files.get('field_name')
を使うのが安全です。ファイルが選択されなかった場合や、指定したname
のフィールドが存在しない場合はNone
が返ります。
“`python
import os
from flask import Flask, request, redirect, url_for, render_template_string
from werkzeug.utils import secure_filename # ファイル名を安全にするためのヘルパー関数
app = Flask(name)
アップロードされたファイルを保存するディレクトリ
UPLOAD_FOLDER = ‘uploads’
app.config[‘UPLOAD_FOLDER’] = UPLOAD_FOLDER
アップロードディレクトリが存在しない場合は作成
if not os.path.exists(UPLOAD_FOLDER):
os.makedirs(UPLOAD_FOLDER)
HTML_UPLOAD_FORM = “””
Upload a File
“””
@app.route(‘/upload’, methods=[‘GET’, ‘POST’])
def upload_file():
if request.method == ‘POST’:
# リクエストにファイルが含まれているかチェック
if ‘uploaded_file’ not in request.files:
return ‘No file part in the request’, 400
file = request.files['uploaded_file'] # FileStorage オブジェクトを取得
# ファイルが選択されているかチェック (ファイル名が空でないか)
if file.filename == '':
return 'No selected file', 400
# ファイルが存在する場合
if file:
# ファイル名を安全にする
filename = secure_filename(file.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
# ファイルをサーバーに保存
file.save(filepath)
return f'File "{filename}" uploaded successfully!'
# GETリクエストの場合はアップロードフォームを表示
return render_template_string(HTML_UPLOAD_FORM)
if name == ‘main‘:
app.run(debug=True)
“`
この例では、
1. POSTメソッドとenctype="multipart/form-data"
が設定されたフォームからファイルを受け取ります。
2. request.files['uploaded_file']
でFileStorageオブジェクトを取得します。
3. ファイルが選択されているか、ファイル名が空でないかを確認します。
4. secure_filename()
を使って、クライアントが送ってきたファイル名を安全なものに変換します。これは、ファイル名に../
のようなパス操作を含む文字列が含まれている場合に、意図しない場所にファイルが保存されるのを防ぐために非常に重要です。
5. file.save(filepath)
で指定したパスにファイルを保存します。
セキュリティ上の注意:
* secure_filename()
は必須です。
* アップロードできるファイルの拡張子やMIMEタイプを制限することで、悪意のあるファイル(例: 実行可能ファイル)のアップロードを防ぐべきです。
* アップロードされたファイルのサイズを制限することも重要です。
第8章: ヘッダー情報の取得
HTTPリクエストヘッダーには、クライアントの種類、要求するレスポンスの形式、認証情報、リファラなど、リクエストに関する重要なメタデータが含まれています。
8.1 request.headers
の使い方
Flaskでは、リクエストヘッダーはrequest.headers
というオブジェクトを通じてアクセスできます。これはEnvironHeaders
というクラスのインスタンスで、辞書ライクにヘッダー名(キー)とその値を取得できます。ヘッダー名は大文字・小文字を区別しません(例: User-Agent
でもuser-agent
でもアクセス可能)。
こちらも、存在しないヘッダーにアクセスする際にエラーを防ぐため、get()
メソッドを使うのが良いでしょう。
“`python
from flask import Flask, request
app = Flask(name)
@app.route(‘/headers’)
def show_headers():
# いくつかの主要なヘッダーを取得して表示
user_agent = request.headers.get(‘User-Agent’) # クライアントのブラウザやOS情報など
referer = request.headers.get(‘Referer’) # どのページからリンクをたどって来たか
content_type = request.headers.get(‘Content-Type’) # リクエストボディの形式
accept_language = request.headers.get(‘Accept-Language’) # クライアントが受け入れ可能な言語
output = f"<h2>Request Headers</h2>"
output += f"<p><b>User-Agent:</b> {user_agent}</p>"
output += f"<p><b>Referer:</b> {referer}</p>"
output += f"<p><b>Content-Type:</b> {content_type}</p>"
output += f"<p><b>Accept-Language:</b> {accept_language}</p>"
# 全てのヘッダーを表示
output += "<h3>All Headers:</h3>"
output += "<ul>"
for header_name, header_value in request.headers.items():
output += f"<li><b>{header_name}:</b> {header_value}</li>"
output += "</ul>"
return output
if name == ‘main‘:
app.run(debug=True)
“`
/headers
にブラウザでアクセスすると、ブラウザの種類やOS、言語設定など、ブラウザが送信した様々なヘッダー情報が表示されます。
第9章: クッキー情報の取得
クッキー(Cookies)は、サーバーがクライアントのブラウザに保存させる小さなデータです。これにより、ログイン状態の維持、ユーザー設定の記憶、トラッキングなどが可能になります。リクエスト時には、クライアントはそのドメインに関連付けられたクッキーをサーバーに自動的に送信します。
9.1 クッキーとは?
クッキーはキーと値のペアで、有効期限、関連付けられるドメインとパス、Secure属性(HTTPSでのみ送信)、HttpOnly属性(JavaScriptからのアクセスを制限)などの情報を持っています。
サーバーはレスポンスヘッダーのSet-Cookie
を使ってクライアントにクッキーを設定するよう指示します。クライアントは次回以降、そのクッキーが関連付けられたドメインへのリクエスト時に、リクエストヘッダーのCookie
にクッキー情報を含めて送信します。
9.2 request.cookies
の使い方
Flaskでは、クライアントから送信されたクッキーはrequest.cookies
というオブジェクトを通じてアクセスできます。これもまたImmutableMultiDictです。
request.cookies
のキーはクッキーの名前、値はクッキーの値(文字列)です。
“`python
from flask import Flask, request, make_response
app = Flask(name)
@app.route(‘/set_cookie’)
def set_cookie():
response = make_response(“Cookie ‘username’ has been set!”)
# レスポンスに Set-Cookie ヘッダーを追加
response.set_cookie(‘username’, ‘Alice’, max_age=3600) # 有効期限1時間 (秒)
return response
@app.route(‘/get_cookie’)
def get_cookie():
# request.cookies からクッキーを取得
username = request.cookies.get(‘username’)
if username:
return f'Welcome back, {username}!'
else:
return 'Hello, stranger! No username cookie found.'
if name == ‘main‘:
app.run(debug=True)
“`
- まず
/set_cookie
にアクセスします。これにより、ブラウザにusername=Alice
というクッキーが設定されます。 - 次に
/get_cookie
にアクセスします。ブラウザは/get_cookie
へのリクエスト時に設定されたクッキーを送信するため、サーバーはrequest.cookies.get('username')
でAlice
という値を取得し、「Welcome back, Alice!」と表示します。 - クッキーを設定せずに
/get_cookie
にアクセスした場合、「Hello, stranger! No username cookie found.」と表示されます。
クッキーの値は常に文字列として取得されることに注意してください。数値を保存したい場合でも、文字列として保存・取得し、アプリケーション側で数値に変換する必要があります。
第10章: リクエストボディとその他の情報
フォームデータやJSONデータ以外にも、リクエストボディに含まれる生のデータにアクセスしたり、クライアントのIPアドレスやアクセス元のURLなどの情報を取得したりすることも可能です。
10.1 request.data
(raw body data)
POSTやPUTなどのリクエストで、application/x-www-form-urlencoded
やapplication/json
以外の形式(例: text/plain
, XML, バイナリデータなど)でデータが送信された場合、または生のデータ全体にアクセスしたい場合は、request.data
属性を使用します。
request.data
は、リクエストボディ全体の生データをバイト列 (bytes
) として保持します。
“`python
from flask import Flask, request
app = Flask(name)
@app.route(‘/raw_data’, methods=[‘POST’])
def process_raw_data():
# リクエストボディの生データを取得(バイト列)
raw_body = request.data
# バイト列を文字列として扱いたい場合はデコードが必要
# エンコーディングは Content-Type ヘッダーなどから判断することが多い
# ここでは例として UTF-8 と仮定
try:
decoded_body = raw_body.decode('utf-8')
return f'Received raw data (UTF-8 decoded): {decoded_body}'
except UnicodeDecodeError:
return f'Received raw data (cannot decode as UTF-8): {raw_body}', 400
if name == ‘main‘:
app.run(debug=True)
“`
このエンドポイントに、例えばcurl -X POST -d "hello world" localhost:5000/raw_data
のようにリクエストを送信すると、「Received raw data (UTF-8 decoded): hello world」と表示されます。
10.2 クライアント情報
request
オブジェクトは、クライアントに関する情報も提供します。
request.remote_addr
: クライアントのIPアドレス(文字列)。ただし、プロキシを経由している場合はプロキシのIPアドレスになることがあります。真のクライアントIPを取得するには、X-Forwarded-Forなどのヘッダーを確認する必要がありますが、これらは信頼できない場合もあります。request.user_agent
: クライアントのユーザーエージェント情報を含むオブジェクト。ブラウザ名、OS、バージョンなどの属性を持ちます。request.headers.get('User-Agent')
よりも構造化された情報にアクセスできます。
“`python
from flask import Flask, request
app = Flask(name)
@app.route(‘/info’)
def show_info():
client_ip = request.remote_addr
# request.user_agent は UserAgent クラスのインスタンス
user_agent_string = request.user_agent.string # 元のユーザーエージェント文字列
browser_name = request.user_agent.browser
platform = request.user_agent.platform
version = request.user_agent.version
output = f"<h2>Client Info</h2>"
output += f"<p><b>Your IP Address:</b> {client_ip}</p>"
output += f"<p><b>User Agent String:</b> {user_agent_string}</p>"
output += f"<p><b>Browser:</b> {browser_name}</p>"
output += f"<p><b>Platform:</b> {platform}</p>"
output += f"<p><b>Version:</b> {version}</p>"
return output
if name == ‘main‘:
app.run(debug=True)
“`
/info
にアクセスすると、あなたのIPアドレスや使用しているブラウザ、OSなどの情報が表示されます。
10.3 URL関連情報
リクエストされたURL自体に関する情報もrequest
オブジェクトから取得できます。
request.url
: スキーム(http/https)、ホスト、ポート、パス、クエリストリング、フラグメントを含む完全なURL文字列。request.base_url
: スキーム、ホスト、ポート、パスを含むが、クエリストリングとフラグメントを含まないURL文字列。request.path
: ホスト名やクエリストリングを含まない、URLのパス部分のみ。request.full_path
: パス部分にクエリストリングを含んだ文字列。
“`python
from flask import Flask, request
app = Flask(name)
@app.route(‘/url_info’)
def show_url_info():
output = f”
URL Info
”
output += f”
request.url: {request.url}
”
output += f”
request.base_url: {request.base_url}
”
output += f”
request.path: {request.path}
”
output += f”
request.full_path: {request.full_path}
”
output += f”
request.host: {request.host}
” # ホスト名とポート
output += f”
request.scheme: {request.scheme}
” # スキーム (http/https)
return output
if name == ‘main‘:
app.run(debug=True)
“`
/url_info?param=value
のようなURLでアクセスしてみると、それぞれの属性が示す内容の違いがよく分かります。
第11章: リクエスト処理におけるエラーハンドリング
クライアントからのリクエストが常にサーバーの期待する形式で送られてくるとは限りません。無効なデータ形式、必須パラメータの不足、許可されていないメソッドなど、様々な問題が発生する可能性があります。これらのエラーに適切に対応し、クライアントに分かりやすいフィードバックを返すことは、堅牢なWebアプリケーションを構築する上で重要です。
11.1 よくあるリクエスト関連のエラー
- 400 Bad Request: クライアントからのリクエストの形式が不正である場合。例えば、期待されるJSONデータが無効な形式だったり、必須のフォームデータが送信されなかったりする場合など。
- 404 Not Found: リクエストされたURLに対応するルートがサーバーに存在しない場合。これはFlaskのルーティングで自動的に処理されることが多いです。
- 405 Method Not Allowed: リクエストされたURLは存在するが、そのURLで指定されたHTTPメソッドが許可されていない場合。これも
@app.route(methods=...)
の設定に基づき、Flaskが自動的に処理します。
11.2 Flaskでのエラーハンドリング
Flaskでは、特定のエラーステータスコードに対するカスタムレスポンスを定義することができます。これは@app.errorhandler()
デコレーターを使用します。これにより、Flaskがデフォルトで返すエラーページではなく、独自のページを表示させることができます。
“`python
from flask import Flask, render_template_string, jsonify
app = Flask(name)
404 Not Found エラーのハンドリング
@app.errorhandler(404)
def page_not_found(error):
return render_template_string(‘
404 Not Found
The requested URL was not found on the server.
‘), 404
405 Method Not Allowed エラーのハンドリング
@app.errorhandler(405)
def method_not_allowed(error):
return render_template_string(‘
405 Method Not Allowed
The method is not allowed for the requested URL.
‘), 405
400 Bad Request エラーのハンドリング
これは特に、無効なJSONなどが送られてきた場合にFlaskが自動的に返すエラー
@app.errorhandler(400)
def bad_request(error):
# APIの場合はJSONでエラー情報を返すのが一般的
if request.path.startswith(‘/api/’):
return jsonify({“error”: “Bad Request”, “message”: str(error)}), 400
else:
return render_template_string(‘
400 Bad Request
Your request was malformed.
‘), 400
例: ルートが存在しない、またはメソッドが許可されていない場合
@app.route(‘/’)
def index():
return “Go to /nonexistent_page or try a wrong method on /api/data”
@app.route(‘/api/data’, methods=[‘POST’])
def process_data():
# JSONを期待するが、無効なJSONが送られたら 400 Bad Request (Flask自動処理)
data = request.get_json()
return jsonify({“received”: data})
if name == ‘main‘:
app.run(debug=True)
“`
この例では、@app.errorhandler()
を使って404, 405, 400エラーが発生した際に、それぞれ定義した関数が実行されるようにしています。特に400エラーハンドラでは、APIエンドポイント(/api/
で始まるパス)の場合はJSONレスポンスを返すように切り替えています。
11.3 入力検証の重要性 (Input Validation)
クライアントから受け取ったデータ(クエリストリング、フォームデータ、JSONなど)は、常に信頼できるとは限りません。悪意のあるデータや、単にユーザーが間違って入力したデータが含まれている可能性があります。これらのデータを利用する前に、必ず検証(Validation)を行うべきです。
入力検証の主な目的は以下の通りです。
- セキュリティ: 不正なデータによってSQLインジェクションやクロスサイトスクリプティング(XSS)などの脆弱性が引き起こされるのを防ぎます。
- データの整合性: アプリケーションが期待する形式や範囲のデータのみを処理し、データの破損や予期せぬエラーを防ぎます。
- ユーザーエクスペリエンス: 入力ミスがあった場合に、具体的なエラーメッセージをクライアントに返すことで、ユーザーが問題を修正しやすくなります。
Flask自体には高度な入力検証機能は組み込まれていませんが、WTFormsやMarshmallow、Pydanticなどのライブラリを組み合わせて使用するのが一般的です。
例(シンプルなPythonの検証):
“`python
from flask import Flask, request, jsonify
app = Flask(name)
@app.route(‘/submit_profile’, methods=[‘POST’])
def submit_profile():
name = request.form.get(‘name’)
age_str = request.form.get(‘age’)
errors = {}
if not name:
errors['name'] = 'Name is required.'
elif len(name) > 50:
errors['name'] = 'Name is too long.'
if not age_str:
errors['age'] = 'Age is required.'
else:
try:
age = int(age_str)
if age < 0 or age > 120:
errors['age'] = 'Age must be between 0 and 120.'
except ValueError:
errors['age'] = 'Age must be a valid number.'
if errors:
# 検証エラーがある場合、エラーメッセージを返す
return jsonify({"errors": errors}), 400
else:
# 検証成功
return jsonify({"message": "Profile submitted successfully", "name": name, "age": age}), 200
if name == ‘main‘:
app.run(debug=True)
“`
この例では、単純なif文とtry-exceptブロックを使って、名前と年齢のフィールドを検証しています。実際には、専用のライブラリを使うことで、より宣言的に、かつ効率的に複雑な検証ルールを記述できます。
第12章: まとめと次のステップ
この記事では、Flaskにおけるリクエスト処理の基本から応用までを、flask.request
オブジェクトを中心に詳細に解説しました。Webアプリケーションの根幹であるクライアントからのリクエストを受け付け、その内容を理解し、適切に処理するための様々な手法を学びました。
重要なポイントを振り返りましょう。
- Webアプリケーションはクライアント・サーバーモデルで動作し、HTTPプロトコルで通信します。
- Flaskでは、現在のリクエスト情報は
flask.request
オブジェクトを通じて取得できます。 request
オブジェクトはリクエストコンテキストによって、同時に複数のリクエストが処理されても安全にアクセスできます。request.method
でHTTPメソッド(GET, POSTなど)を判別し、処理を分岐させることができます。- URLパス中の変数はルート定義でキャプチャし、ビュー関数の引数として取得できます。
- クエリストリングのデータは
request.args
から取得します。get()
メソッドを安全に使用しましょう。 - HTMLフォームからのPOSTデータは
request.form
から取得します。こちらもget()
メソッドが便利です。 - JSON形式のデータは
request.json
またはrequest.get_json()
でパースして取得します。パースエラーに注意しましょう。 - アップロードされたファイルは
request.files
からFileStorage
オブジェクトとして取得し、save()
メソッドなどで保存できます。secure_filename()
によるファイル名の安全化は必須です。 - リクエストヘッダーは
request.headers
から取得できます。 - クッキー情報は
request.cookies
から取得できます。 request.data
でリクエストボディの生データにアクセスできます。request.remote_addr
でクライアントIP、request.user_agent
でユーザーエージェント情報を取得できます。request.url
,request.path
などでリクエストされたURLの詳細情報を取得できます。- リクエスト処理においては、無効なリクエスト形式やデータ不足などのエラーに備え、エラーハンドリングを適切に行うことが重要です。
@app.errorhandler()
を活用しましょう。 - クライアントからの入力データは常に検証(Input Validation)を行い、セキュリティとデータの整合性を確保することが不可欠です。
これで、あなたはFlaskアプリケーションがどのようにクライアントと対話し、リクエストに含まれる様々な情報を取り出すのかについて、しっかりとした基礎を築くことができたはずです。
しかし、Webアプリケーション開発はリクエスト処理だけでは完結しません。クライアントに結果を返す「レスポンス処理」も同様に重要です。HTML、JSON、リダイレクト、ファイルの送信、ステータスコードやヘッダーの設定など、レスポンスに関する様々な要素があります。
次のステップとして、Flaskのレスポンス処理について学ぶことをお勧めします。 リクエストで情報を受け取り、レスポンスで結果を返す、このサイクル全体をマスターすることが、フルスタックのWebアプリケーション開発能力につながります。
Flaskの公式ドキュメントや、他のチュートリアル、さらには興味のあるライブラリ(データベース操作のためのSQLAlchemy、フォーム検証のためのWTFormsなど)についても学びを深めていくことで、あなたのFlask開発スキルはさらに向上するでしょう。
この記事が、あなたのFlask学習の助けとなれば幸いです。 Happy coding!