HttpRequestとHttpResponseの基礎概念
Webアプリケーションの根幹を成すのがHTTPのリクエストとレスポンスのサイクルである。クライアントがURLを通じてサーバーへ要求を送信し、サーバーがその処理結果を返すというこの流れを、Djangoではオブジェクト指向的なアプローチで扱う。
HttpRequestオブジェクトの概要
Djangoにリクエストが到達すると、フレームワークは生のHTTPリクエストデータをHttpRequestオブジェクトとしてカプセル化する。このオブジェクトには、リクエストメソッドやヘッダー、パラメータなど、クライアントからのすべての情報が格納される。
def handle_input(incoming_req):
# incoming_reqはHttpRequestのインスタンス
print(incoming_req.method) # "GET"や"POST"などのメソッドを取得HttpResponseオブジェクトの概要
Djangoのビューは、処理を完結させた後に必ずHttpResponseオブジェクトを返さなければならない。このオブジェクトがクライアントへ送信される実際のHTTPレスポンスへと変換される。
from django.http import HttpResponse
def greet_handler(req):
resp = HttpResponse("Welcome, User!")
return resp # このレスポンスがクライアントへ送信されるHTTPメソッドの判定
クライアントの意図を識別するため、HTTPメソッド(GETによるリソース取得、POSTによるリソース更新など)を確認することが頻繁に行われる。Djangoではmethod属性からこれを取得できる。
def inspect_request(req):
print(req.method) # "GET" や "POST" を出力リクエストのライフサイクル
Djangoアプリケーションがリクエストを受信してからレスポンスを返すまでには、明確な段階を踏む。
WSGIサーバーへの到達
最初にリクエストを受け取るのはWSGIサーバー(GunicornやuWSGIなど)である。これらはPythonのWSGI仕様に基づき、HTTPリクエストをDjangoアプリケーションへ橋渡しする。
# WSGIアプリケーションの簡易例
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return [b"Greetings!"]URLルーティング
リクエストはURLディスパッチャに渡され、urls.pyで定義されたパターンと照合される。マッチしたパターンが見つかると、対応するビュー関数へ処理が移行する。
# urls.py
from django.urls import path
from . import views
urlpatterns = [
path('posts/2020/', views.archive_2020),
path('posts/<int:pub_year>/', views.yearly_posts),
]ビューの実行
ルーティングによって呼び出されたビュー関数は、HttpRequestとURLから抽出された引数を受け取り、ビジネスロジックを実行する。
# views.py
from django.http import HttpResponse
def archive_2020(req):
return HttpResponse("Archived posts for 2020")
def yearly_posts(req, pub_year):
return HttpResponse(f"Posts published in {pub_year}")レスポンスの返却
ビューが生成したHttpResponseオブジェクトは、再びミドルウェアなどを通過しながら最終的なHTTPレスポンスへと変換され、クライアントへ返される。
HttpRequestの詳細
HttpRequestオブジェクトは、リクエストに関するあらゆる情報へのアクセスを提供する。
主要な属性
- path: ドメインを除いたリクエストパス(例:
/api/data/)。 - method: HTTPメソッドを示す文字列。
- GET: GETパラメータを保持する辞書風オブジェクト。
- POST: POSTパラメータを保持する辞書風オブジェクト。
- COOKIES: クライアントから送信されたCookieの辞書。
- FILES: アップロードされたファイル情報。
- user: 認証済みのユーザーオブジェクト。未認証の場合は
AnonymousUserとなる。
def analyze_request(req):
print(req.path) # "/api/data/"
print(req.method) # "GET"
print(req.GET) # <QueryDict: {'key': ['value']}>
print(req.user) # AnonymousUser または Userインスタンス有用なメソッド
- is_ajax(): XMLHttpRequestによるリクエストか判定。
- is_secure(): HTTPS接続か判定。
- is_authenticated(): ユーザーが認証済みか判定。
def check_request_properties(req):
print(req.is_ajax()) # AJAXリクエストならTrue
print(req.is_secure()) # HTTPSならTrue
print(req.is_authenticated()) # ログイン済みならTrueビューとリクエスト処理の関連
ビューの役割は、HttpRequestを受け取り、ロジックを実行し、HttpResponseを返すことにある。
ビューの定義
第一引数にHttpRequestを取る関数として定義する。
from django.http import HttpResponse
def greet(req):
return HttpResponse("Greetings!")URLパラメータの受け取り
URLから抽出された変数は、ビューの追加引数として渡される。
from django.http import HttpResponse
def greet_user(req, username):
return HttpResponse(f"Hello, {username}!")HttpResponseの構築
HttpResponseはコンテンツのほか、MIMEタイプやステータスコードも同時に設定できる。
from django.http import HttpResponse
def custom_response(req):
resp = HttpResponse("Processed", content_type="text/plain", status=200)
return respHttpResponseの詳細
ビューから返されるHttpResponseは、クライアントへの応答内容を決定づける。
主要な属性
- content: レスポンス本体(文字列またはバイト列)。
- status_code: HTTPステータスコード。
- content_type: MIMEタイプ(デフォルトは
text/html)。
from django.http import HttpResponse
def build_response(req):
resp = HttpResponse()
resp.content = "Dynamic content"
resp.status_code = 200
resp.content_type = 'text/plain'
return respCookie操作メソッド
- set_cookie(key, value, max_age=None, expires=None): Cookieを設定する。
- delete_cookie(key): Cookieを削除する。
from django.http import HttpResponse
def manage_cookies(req):
resp = HttpResponse("Cookie set")
resp.set_cookie('session_token', 'abc123', max_age=86400) # 1日間有効なCookie
return resp特化型レスポンスクラス
Djangoは特定のユースケースに合わせたサブクラスを提供している。
- JsonResponse: JSONデータを返す。
- HttpResponseRedirect: リダイレクトを返す。
- HttpResponseNotFound: 404エラーを返す。
from django.http import JsonResponse, HttpResponseRedirect, HttpResponseNotFound
def api_data(req):
return JsonResponse({'status': 'success'})
def move_page(req):
return HttpResponseRedirect('/new-location/')
def not_found(req):
return HttpResponseNotFound('<h1>Resource Missing</h1>')ビューにおけるレスポンス構築とショートカット
一般的な処理を簡略化するため、Djangoは便利なショートカット関数を用意している。
from django.shortcuts import render, redirect, get_object_or_404
from .models import Article
def list_view(req):
ctx = {'items': ['A', 'B']}
return render(req, 'list_template.html', ctx)
def transfer_view(req):
return redirect('/alternative-path/')
def detail_view(req, item_pk):
record = get_object_or_404(Article, pk=item_pk)
return render(req, 'detail_template.html', {'record': record})リクエスト・レスポンス処理におけるセキュリティ
Webアプリの脆弱性を防ぐため、Djangoは複数の防御機構を備えている。
CSRF防御
POSTリクエストに対してはCSRFトークンの検証がデフォルトで行われる。意図的に無効化する場合はデコレータを使用するが、推奨されない。
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def unsafe_endpoint(req):
passパスワードの安全な保存
生のパスワードはハッシュ化して保存する。
from django.contrib.auth.hashers import make_password
def signup(req):
raw_pw = req.POST['password']
secure_pw = make_password(raw_pw)
# secure_pwをデータベースへ保存セキュアヘッダーの設定
設定ファイルでHTTPヘッダーのセキュリティオプションを有効化できる。
# settings.py
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True入力値の検証
ユーザーからの入力は決して信用せず、必ず検証を行う。
from django.core.exceptions import ValidationError
def submit_feedback(req):
msg = req.POST['feedback']
if len(msg) > 500:
raise ValidationError("Feedback exceeds maximum length.")
# データベースへ保存非同期リクエストとレスポンス
Django 3.1以降、async defを用いた非同期ビューやミドルウェアがサポートされている。I/Oバウンドな処理を効率よく実行できる。
非同期ビュー
from django.http import JsonResponse
async def fetch_async_info(req):
payload = await retrieve_payload() # 非同期関数の呼び出し
return JsonResponse(payload)非同期ミドルウェア
class AsyncMiddlewareExample:
async def __call__(self, req, handler):
resp = await handler(req)
return resp非同期コンテキストでのDBアクセス
非同期ビュー内で同期的なデータベースクエリを実行するとイベントループがブロックされるため、sync_to_asyncでラップする必要がある。
from asgiref.sync import sync_to_async
from django.contrib.auth.models import User
async def get_user_async(req):
fetch_record = sync_to_async(User.objects.get)
record = await fetch_record(pk=1)
return JsonResponse({'user_name': record.username})他のPythonフレームワークとの比較
Django vs Flask
Flaskは軽量でカスタマイズ性が高いマイクロフレームワークである。
- リクエストオブジェクト: Flaskの
requestはグローバルなコンテキストでアクセス可能であり、構文が簡潔(例:request.form['key'])。Djangoは明示的にビューの引数として渡される。 - レスポンスオブジェクト: Flaskはビューから文字列を返すだけで自動的に
Responseに変換されるが、DjangoではHttpResponseのインスタンスを明示的に返す必要がある。 - ルーティング: Flaskはデコレータ内で直感的にパラメータを定義できる。
# Flask
@app.route('/profile/<username>')
def display_profile(username):
return f'Profile of {username}'
# Django
from django.urls import path
def display_profile(req, username):
return HttpResponse(f'Profile of {username}')
urlpatterns = [
path('profile/<str:username>/', display_profile),
]Django vs FastAPI
FastAPIは高速性と非同期ネイティブ、型チェックを特徴とするモダンなフレームワークである。
- 型ヒントとバリデーション: FastAPIは関数の型ヒントから自動的にリクエストデータのバリデーションを行う。Djangoでは手動でのバリデーションが必要。
- 非同期サポート: Djangoも非同期をサポートするようになったが、FastAPIは最初から非同期前提で設計されており、よりシームレスに扱える。
- APIドキュメント: FastAPIはコードからOpenAPI(Swagger)ドキュメントを自動生成する。Djangoで同様のことを行うにはDRFなどの追加ライブラリが必要になる。
# FastAPI
from fastapi import FastAPI
app = FastAPI()
@app.get("/products/{product_id}")
async def fetch_product(product_id: int):
return {"product_id": product_id}