DjangoにおけるHTTPリクエストとレスポンスの処理アーキテクチャ

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 resp

HttpResponseの詳細

ビューから返される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 resp

Cookie操作メソッド

  • 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}

タグ: Django HttpRequest HttpResponse WSGI asgi

9月15日 23:57 投稿