Flask request入門:Webアプリでのリクエスト処理


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は以下の処理を行います。

  1. リクエストの受け取りと解析: Webサーバーは受信した生のHTTPリクエストデータをFlaskに渡します。Flaskはこれを解析し、扱いやすいPythonオブジェクトに変換します。
  2. ルーティング (Routing): リクエストのURLやメソッドに基づき、どのビュー関数(リクエストを処理するPython関数)を実行すべきかを決定します。これは@app.route()デコレーターで定義されたURLルールと照合することで行われます。
  3. リクエストコンテキストの有効化: 適切なビュー関数が見つかったら、Flaskは「リクエストコンテキスト (Request Context)」を有効にします。これにより、そのリクエストの間、どこからでも現在のリクエスト情報にアクセスできるようになります。
  4. ビュー関数の実行: マッチしたビュー関数が実行されます。この関数の中で、開発者はリクエストに含まれる様々な情報(パラメータ、フォームデータなど)にアクセスし、それに応じた処理を行います。
  5. レスポンスの生成: ビュー関数は、処理結果としてレスポンスを返します。これは文字列、HTML、JSON、リダイレクトなどの形式を取り得ます。
  6. レスポンスの送信: Flaskはビュー関数から返されたレスポンスをHTTPレスポンス形式に変換し、Webサーバーを通じてクライアントに送信します。
  7. リクエストコンテキストの破棄: リクエストの処理が完了したら、リクエストコンテキストは無効になります。

この一連の流れの中で、特に開発者が関わるのが「ビュー関数の実行」フェーズです。そして、このフェーズでクライアントから送られてきた情報にアクセスするために使用するのが、次に解説する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

Submit Form

Name:
Message:


“””

@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では、qpageがクエリストリングのキーです。

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 Form

Contact Us

名前:
Eメール:
メッセージ:



“””

@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でないか)、そして必要なキー(nameage)が存在するかをチェックしています。不足があれば適切なエラーレスポンス(ステータスコード400)を返しています。

第7章: ファイルアップロードの処理

ユーザーが画像やドキュメントなどのファイルをサーバーにアップロードできるようにしたい場合、Flaskではこれを簡単に処理できます。

7.1 HTMLフォームの設定(ファイルアップロード用)

ファイルアップロードを含むフォームを送信するには、HTMLの<form>タグで以下の2つの属性を適切に設定する必要があります。

  1. method="post": ファイルデータはリクエストボディで送信されるため、POSTメソッドを使います。
  2. 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 File

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)
“`

  1. まず/set_cookieにアクセスします。これにより、ブラウザにusername=Aliceというクッキーが設定されます。
  2. 次に/get_cookieにアクセスします。ブラウザは/get_cookieへのリクエスト時に設定されたクッキーを送信するため、サーバーはrequest.cookies.get('username')Aliceという値を取得し、「Welcome back, Alice!」と表示します。
  3. クッキーを設定せずに/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-urlencodedapplication/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!


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール