在 Python 3 中,Django 的 APIView
類是 Django REST Framework(DRF)中用于構建 API 視圖的核心基類。它提供了一個靈活的框架來處理 HTTP 請求,并通過一系列方法支持認證、權限檢查和請求限制等功能。
self.perform_authentication(request)
:認證self.check_permissions(request)
:權限驗證self.check_throttles(request)
:請求速率限制
1. self.perform_authentication(request)
作用
self.perform_authentication(request)
用于對傳入的 HTTP 請求進行身份認證(authentication)。它的主要目的是驗證請求是否來自合法用戶,并將認證后的用戶信息(如用戶對象)綁定到 request.user
和 request.auth
屬性上。
request.user
: 通常存儲認證后的用戶對象(例如 Django 的User
模型實例)。如果未認證,默認為AnonymousUser
。request.auth
: 存儲認證過程中可能附加的認證信息,例如 JWT 令牌、OAuth 令牌等。
實現原理
perform_authentication
方法調用了 DRF 的認證流程,依賴于 authentication_classes
屬性中定義的認證類。DRF 的認證機制是模塊化的,允許開發者通過配置不同的認證類(如 SessionAuthentication
、TokenAuthentication
、JWTAuthentication
等)來支持多種認證方式。
在 APIView
類中,perform_authentication
的定義如下(參考 DRF 源碼,基于 3.15 版本):
def perform_authentication(self, request):request.user
看似簡單,但實際邏輯隱藏在 request.user
的屬性訪問中。當訪問 request.user
時,DRF 會觸發以下步驟:
- 獲取認證類:從
self.authentication_classes
獲取配置的認證類列表。這些類繼承自rest_framework.authentication.BaseAuthentication
。 - 逐個調用認證類:按順序調用每個認證類的
authenticate
方法,嘗試對請求進行認證。- 每個
authenticate
方法返回一個(user, auth)
元組,表示認證成功;如果返回None
,則嘗試下一個認證類。 - 如果認證失敗且認證類拋出
AuthenticationFailed
異常,DRF 會立即返回 401 Unauthorized 響應。
- 每個
- 設置認證結果:
- 如果認證成功,
request.user
被設置為認證后的用戶對象,request.auth
被設置為認證令牌或其他上下文信息。 - 如果沒有認證類成功認證,
request.user
默認為AnonymousUser
,request.auth
為None
。
- 如果認證成功,
執行時機
perform_authentication
在 APIView
的 initial
方法中被調用,initial
方法是請求處理流程的初始化階段:
def initial(self, request, *args, **kwargs):self.perform_authentication(request)self.check_permissions(request)self.check_throttles(request)# 其他初始化邏輯
配置認證類
在 APIView
子類中,可以通過 authentication_classes
屬性指定認證方式。例如:
from rest_framework.views import APIView
from rest_framework.authentication import SessionAuthentication, TokenAuthenticationclass MyAPIView(APIView):authentication_classes = [SessionAuthentication, TokenAuthentication]def get(self, request):return Response({"user": request.user.username})
在上述例子中,DRF 會優先嘗試 SessionAuthentication
,如果失敗則嘗試 TokenAuthentication
。
注意事項
- 短路行為:
perform_authentication
不會直接拋出異常,而是依賴認證類的實現。如果所有認證類都返回None
,請求被視為匿名請求。 - 性能考慮:認證可能涉及數據庫查詢(如驗證用戶令牌),因此應盡量優化認證類的實現,避免不必要的性能開銷。
- 自定義認證:可以繼承
BaseAuthentication
創建自定義認證類,并添加到authentication_classes
中。例如:
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailedclass CustomAuth(BaseAuthentication):def authenticate(self, request):auth_header = request.headers.get('Authorization')if not auth_header:return None# 自定義認證邏輯user = validate_token(auth_header) # 假設的 token 驗證函數if not user:raise AuthenticationFailed('Invalid token')return (user, auth_header)
常見使用場景
- 基于令牌的認證:如
TokenAuthentication
或 JWT,用于移動端或單頁應用。 - 會話認證:如
SessionAuthentication
,適用于基于瀏覽器的應用。 - 匿名訪問:允許匿名用戶訪問某些 API(
request.user
為AnonymousUser
)。
2. self.check_permissions(request)
作用
self.check_permissions(request)
用于檢查請求用戶是否具有執行當前操作的權限。它基于 permission_classes
屬性中定義的權限類,決定是否允許請求繼續處理。如果權限檢查失敗,DRF 會拋出 PermissionDenied
異常,導致返回 403 Forbidden 響應。
實現原理
check_permissions
方法會遍歷 self.permission_classes
中定義的權限類,調用每個權限類的 has_permission
方法:
def check_permissions(self, request):for permission in self.get_permissions():if not permission.has_permission(request, self):self.permission_denied(request, message=getattr(permission, 'message', None))
- 權限類:每個權限類繼承自
rest_framework.permissions.BasePermission
,并實現has_permission
方法(對于對象級權限,還可能實現has_object_permission
)。 - 檢查邏輯:
has_permission
方法返回True
表示權限通過,False
表示拒絕。如果任何一個權限類返回False
,DRF 會拋出PermissionDenied
異常。 - 錯誤處理:
permission_denied
方法會生成 403 響應,并包含權限類提供的錯誤信息(如果有)。
執行時機
check_permissions
在 initial
方法中,在 perform_authentication
之后調用。這是因為權限檢查通常依賴于認證結果(例如,檢查 request.user
是否有特定權限)。
配置權限類
可以通過 permission_classes
屬性指定權限。例如:
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated, IsAdminUserclass MyAPIView(APIView):permission_classes = [IsAuthenticated, IsAdminUser]def get(self, request):return Response({"message": "You have admin access"})
在上述例子中,用戶必須同時通過 IsAuthenticated
(已登錄)和 IsAdminUser
(管理員權限)檢查才能訪問 API。
內置權限類
DRF 提供了一些常用的權限類:
AllowAny
: 允許所有用戶(包括匿名用戶)訪問。IsAuthenticated
: 僅允許已認證用戶訪問。IsAdminUser
: 僅允許管理員用戶訪問。IsAuthenticatedOrReadOnly
: 允許匿名用戶的只讀訪問(GET、HEAD、OPTIONS),但寫操作需要認證。
自定義權限
可以創建自定義權限類。例如,限制只有特定角色的用戶可以訪問:
from rest_framework.permissions import BasePermissionclass IsPremiumUser(BasePermission):def has_permission(self, request, view):return request.user.is_authenticated and request.user.is_premium
注意事項
- 權限順序:權限類按順序檢查,建議將更嚴格的權限放在前面以提高效率。
- 對象級權限:對于需要檢查特定對象的權限(如編輯某個資源),可以在視圖中調用
self.check_object_permissions(request, obj)
,觸發權限類的has_object_permission
方法。 - 錯誤信息:可以通過權限類的
message
屬性自定義錯誤提示。例如:
class IsPremiumUser(BasePermission):message = "You must be a premium user to access this resource."def has_permission(self, request, view):return request.user.is_authenticated and request.user.is_premium
常見使用場景
- 限制訪問:僅允許認證用戶或特定角色(如管理員、訂閱者)訪問 API。
- 只讀訪問:允許匿名用戶讀取數據,但限制寫操作。
- 動態權限:根據請求方法(GET、POST 等)或資源類型動態調整權限。
3. self.check_throttles(request)
作用
self.check_throttles(request)
用于限制請求速率(rate limiting),防止 API 被濫用。它基于 throttle_classes
屬性中定義的限制類,檢查用戶或 IP 的請求頻率是否超出限制。如果超出限制,DRF 會拋出 Throttled
異常,導致返回 429 Too Many Requests 響應。
實現原理
check_throttles
方法會遍歷 self.throttle_classes
中定義的限制類,調用每個限制類的 allow_request
方法:
def check_throttles(self, request):for throttle in self.get_throttles():if not throttle.allow_request(request, self):self.throttled(request, throttle.wait())
- 限制類:每個限制類繼承自
rest_framework.throttling.BaseThrottle
,并實現allow_request
方法,決定是否允許當前請求。 - 檢查邏輯:
allow_request
返回True
表示請求允許,False
表示請求被限制。如果被限制,throttle.wait()
返回預計的等待時間(秒),并包含在 429 響應中。 - 緩存支持:DRF 的限制機制通常依賴緩存(如 Redis 或 Django 的內存緩存)來跟蹤請求頻率。
執行時機
check_throttles
在 initial
方法中,在 perform_authentication
和 check_permissions
之后調用。這是因為速率限制通常需要基于用戶身份或 IP 地址,而這些信息在認證后更明確。
配置限制類
可以通過 throttle_classes
屬性指定限制。例如:
from rest_framework.views import APIView
from rest_framework.throttling import UserRateThrottleclass MyAPIView(APIView):throttle_classes = [UserRateThrottle]def get(self, request):return Response({"message": "Request allowed"})
內置限制類
DRF 提供了一些常用的限制類:
AnonRateThrottle
: 限制匿名用戶的請求速率,基于 IP 地址。UserRateThrottle
: 限制認證用戶的請求速率,基于用戶 ID。ScopedRateThrottle
: 限制特定視圖的請求速率,可以通過throttle_scope
屬性配置。
限制速率在 settings.py
中通過 REST_FRAMEWORK
配置。例如:
REST_FRAMEWORK = {'DEFAULT_THROTTLE_CLASSES': ['rest_framework.throttling.AnonRateThrottle','rest_framework.throttling.UserRateThrottle',],'DEFAULT_THROTTLE_RATES': {'anon': '100/day', # 匿名用戶每天 100 次請求'user': '1000/day', # 認證用戶每天 1000 次請求}
}
自定義限制
可以創建自定義限制類。例如,限制特定用戶組的請求速率:
from rest_framework.throttling import SimpleRateThrottleclass PremiumUserThrottle(SimpleRateThrottle):scope = 'premium'def get_cache_key(self, request, view):if request.user.is_authenticated and request.user.is_premium:return f"throttle_{request.user.id}"return None
在 settings.py
中配置:
REST_FRAMEWORK = {'DEFAULT_THROTTLE_RATES': {'premium': '5000/day',}
}
注意事項
- 緩存依賴:速率限制通常需要配置緩存后端(如 Redis),否則可能無法正常工作。
- 匿名用戶限制:基于 IP 的限制可能不準確,因為多個用戶可能共享同一 IP(如代理服務器)。
- 動態限制:可以通過
throttle_scope
和自定義限制類實現基于視圖或用戶的動態限制。 - 錯誤響應:429 響應會包含
Retry-After
頭,指示客戶端需要等待的時間。
常見使用場景
- 防止濫用:限制匿名用戶對公開 API 的頻繁訪問。
- 分級服務:為不同用戶組(如免費用戶、付費用戶)設置不同的請求速率。
- 保護資源:避免服務器因高頻請求過載。
綜合分析與優化建議
執行順序的意義
perform_authentication
、check_permissions
和 check_throttles
的調用順序是有意設計的:
- 先認證:確保
request.user
和request.auth
已設置,因為權限和限制可能依賴這些信息。 - 再檢查權限:基于認證結果判斷用戶是否有權訪問資源。
- 最后限制速率:確保只有合法請求才會計入速率限制,避免浪費配額。
性能優化
- 認證:選擇輕量級的認證方式(如 JWT 比 Session 更適合無狀態 API),并緩存頻繁查詢的用戶數據。
- 權限:將復雜的權限檢查邏輯移到對象級(
has_object_permission
),減少視圖級檢查的開銷。 - 限制:使用高效的緩存后端(如 Redis),并合理設置限制周期(如小時、天)以平衡性能和安全性。
擴展性
- 模塊化設計:DRF 的認證、權限和限制機制高度模塊化,允許通過自定義類實現復雜邏輯。
- 全局配置:在
settings.py
中通過REST_FRAMEWORK
設置默認的認證、權限和限制類,減少重復代碼。 - 動態調整:通過
get_authenticators
、get_permissions
和get_throttles
方法動態返回認證、權限或限制類,實現基于視圖或請求的靈活配置。
常見問題與解決方案
- 401 vs. 403:
- 401 Unauthorized:認證失敗(如無效的令牌)。
- 403 Forbidden:認證成功但權限不足。
- 解決方案:確保認證和權限類的錯誤信息清晰,方便調試。
- 速率限制不生效:檢查是否正確配置了緩存后端(如
CACHES
設置)。 - 匿名用戶問題:明確區分匿名和認證用戶的處理邏輯,避免意外允許敏感操作。
實際案例
假設你正在開發一個博客 API,需求如下:
- 匿名用戶可以讀取文章(GET)。
- 認證用戶可以創建文章(POST),但每天限制 10 次請求。
- 管理員可以編輯所有文章(PUT)。
實現代碼如下:
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.throttling import UserRateThrottle
from rest_framework.response import Responseclass ArticleAPIView(APIView):authentication_classes = [TokenAuthentication]permission_classes = [IsAuthenticated]throttle_classes = [UserRateThrottle]def get_permissions(self):if self.request.method == 'GET':return [AllowAny()]elif self.request.method == 'PUT':return [IsAdminUser()]return super().get_permissions()def get(self, request):return Response({"message": "List of articles"})def post(self, request):return Response({"message": "Article created"})def put(self, request):return Response({"message": "Article updated"})
在 settings.py
中:
REST_FRAMEWORK = {'DEFAULT_THROTTLE_RATES': {'user': '10/day',}
}
總結
perform_authentication
:負責用戶認證,將用戶信息綁定到請求對象,依賴authentication_classes
。check_permissions
:檢查用戶權限,決定是否允許訪問,依賴permission_classes
。check_throttles
:限制請求速率,防止濫用,依賴throttle_classes
。- 設計哲學:DRF 通過模塊化的認證、權限和限制機制,提供靈活且可擴展的 API 開發框架。
- 優化與實踐:合理配置認證、權限和限制類,結合緩存和動態調整,滿足復雜業務需求。