`django.utils.http`是 Django 開發者不可或缺且非常實用的常用模塊。 這個模塊集合了處理 HTTP 協議本身或安全地操作 URL 所需的核心工具函數。其重點在於構建 URL 和查詢字符串,並在 HTTP 環境中*安全地*傳遞數據,而不是*發送* HTTP 請求(例如:`requests`庫)。 這篇文章將探討`django.utils.http`的核心功能。 ![django http util deep dive image](/media/whitedec/blog_img/c0fa237054df4f8eb25c18785e3a6426.webp) --- ## 1. 適用於 URL 的安全 Base64 編碼/解碼: `urlsafe_base64_encode` / `decode` {#sec-b1d59d77d6ff} 這個模塊的亮點之一。 一般的 Base64 編碼會包含`+`或`/`等特殊字符。這些字符在 URL 中有特殊的意義(例如空格、路徑分隔符等),因此在通過 URL 傳遞值時可能會導致問題。 `urlsafe_base64_encode`在編碼時,會將這些字符替換為 URL 中安全的字元 `-` 和 `_`。 **主要使用情境:** * 基於電子郵件的註冊驗證令牌 * 用於密碼重設連結的用戶 ID (pk) 編碼 **範例:** (生成密碼重設鏈接) ```python from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from django.utils.encoding import force_bytes, force_str from django.contrib.auth.models import User # 1. 在生成令牌時(編碼) user = User.objects.get(username='testuser') # 重要:編碼前必須始終轉換為 bytes。 uid_bytes = force_bytes(user.pk) uid_token = urlsafe_base64_encode(uid_bytes) print(f"編碼的令牌: {uid_token}") # 例如: 'Mg' (假設 user.pk 是 2) # reset_url = f"/password-reset/{uid_token}/..." # 2. 當用戶點擊鏈接時(解碼) try: # 將從 URL 接收到的令牌解碼回 bytes uid_bytes_decoded = urlsafe_base64_decode(uid_token) # 轉換回字符串(或整數) user_pk = force_str(uid_bytes_decoded) user = User.objects.get(pk=user_pk) print(f"恢復的 PK: {user.pk}") except (TypeError, ValueError, OverflowError, User.DoesNotExist): user = None ``` > **注意:** `force_bytes` 和 `force_str` 在 `django.utils.encoding` 模塊中,通常與 `urlsafe_base64` 函數一起使用。 --- ## 2. 查詢字符串生成器: `urlencode` {#sec-39f1997d2d03} 將 Python 字典(dict) 對象方便地轉換為 HTTP GET 請求所使用的查詢字符串(例如:`?key=value&key2=value2`)。 **範例:** ```python from django.utils.http import urlencode params = { 'page': 3, 'q': 'django rest framework', 'region': 'ja' } # 將字典的鍵和值進行 URL 編碼以生成查詢字符串。 # 'django rest framework' 的空格將安全地編碼為 '+' 或 '%20'。 query_string = urlencode(params) print(query_string) # 輸出: 'page=3&q=django+rest+framework®ion=ja' ``` --- ## 3. 驗證安全重定向: `is_safe_url` {#sec-4292dd7f0891} 這是一個安全性至關重要的功能。用於防止“開放重定向”漏洞。 登錄後,通常會將用戶重定向回他們先前所在的頁面,例如`?next=/profile/`。如果攻擊者將外部 URL(如`?next=http://malicious-site.com`)插入`next`值,則在用戶完成登入後可能會將其導向惡意網站。 `is_safe_url`會檢查給定的 URL 是否為當前應用程式主機(網域)下的「安全」URL,或是否為相對路徑。 **範例:** (登錄視圖) ```python from django.utils.http import is_safe_url from django.shortcuts import redirect, resolve_url def login_success_redirect(request): # 從 GET 參數中獲取 'next' 值。 next_url = request.GET.get('next') # 確保 next 值存在且 # 必須通過 is_safe_url 檢查才能重定向到該 URL。 if next_url and is_safe_url( url=next_url, allowed_hosts={request.get_host()}, # 只允許當前請求的主機 require_https=request.is_secure() # 如果當前請求是 HTTPS,則重定向也必須是 HTTPS ): return redirect(next_url) # 如果不安全或不存在 'next' 值,則重定向到默認頁面。 return redirect(resolve_url('main-dashboard')) ``` --- ## 4. 其他有用的函數 {#sec-d24ea3284f36} * **`urlquote(value)` / `urlunquote(value)`:** `urlencode`將整個字典轉換為查詢字符串,而`urlquote`則將**單一字符串**中的特殊字符(空格、CJK 字符等)編碼為`%XX`形式(百分比編碼)。這在構建 URL 路徑(path)的某一部分時很有用。 * **`parse_http_date(date_str)` / `http_date(timestamp)`:** 用於解析或生成`Last-Modified`或`Expires`等 HTTP 標頭中使用的標準日期格式(RFC 1123)字符串。 --- ## 總結 {#sec-4dd87cd52c55} `django.utils.http`是一個出色的範例,展示了 Django 如何深入處理 HTTP 標準。 特別是,使用**`urlsafe_base64_encode`** 進行安全的令牌傳遞以及透過**`is_safe_url`** 確保重定向的安全性,這些都是所有 Django 開發者必須了解的核心功能。有效利用這個模塊可以使 HTTP 數據處理更加安全和穩健。