rest_framework框架的封裝特點
# 導入 rest_framework
# rest_framework的導入風格就是按照英文的意思就可以了。
import rest_framework
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.exceptions import APIException
from rest_framework.pagination import PageNumberPagination
from rest_framework.settings import APISettings
from rest_framework.parsers import JSONParser
from rest_framework.filters import OrderingFilter
原生Django與DRF比較
原生Django
from django.views import View
from django.http import JsonResponse
class BookView(View):
def get(self, request, *args, **kwargs):
return JsonResponse({
'msg': 'view get ok'
})
def post(self, request, *args, **kwargs):
return JsonResponse({
'msg': 'view post ok'
})
drf
from rest_framework.views import APIView
from rest_framework.response import Response
class BookAPIView(APIView):
def get(self, request, *args, **kwargs):
return Response({
'msg': 'apiview get ok'
})
def post(self, request, *args, **kwargs):
return Response({
'msg': 'apiview post ok'
})
django的配置文件中的CSRF認證注釋去掉。
測試原生的Django接口和DRF接口發現:
原生Django接口提交post請求會被django拒絕
用DRF寫的接口不會被拒
我們只有看了源碼才知道,是因為DRF接口繼承了APIView,內部重寫了原生View類,並添加了新功能:局部禁用csrf
view = super().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
# 局部禁用csrf
return csrf_exempt(view)
APIView 的請求生命周期
-
APIView 類繼承View類,重寫了as_view和dispatch方法
-
APIView類重寫的as_view()方法本質上還是View的as_view,只是在返回視圖view函數地址時,局部禁用了csrf認證
-
APIView中重寫dispatch方法:
- 在執行請求邏輯前,執行請求模塊(二次封裝request對象),解析模塊(三種數據包格式的數據解析)
- 在執行請求邏輯中,執行邏輯,若出現異常交給異常模塊處理
- 在執行請求邏輯后,執行響應模塊(二次封裝response)、渲染模塊(響應的數據能夠使用JSON和頁面兩種方式渲染)
請求模塊(request)
-
將wsgi的request對象二次封裝,轉換成drf的Request類的對象
-
drf封裝后的request對象完全兼容wsgi的request對象,並且保存原request對象在新request._request方法中:
new_request.__dict__ = request.__dict new_request._request = request
-
重新格式化請求數據存放位置
拼接參數:request.query_params
數據包參數:request.data
源碼分析:
1. 入口:APIVIew的dispatch方法的 request=self.initialize_request(request, *args, **kwargs)
2. print(request._request.method) # 在內部將wsgi的request賦值給request._request
3. print(request.method) # 就是通過__getattr__走的是request._request.method
4. print(request.query_params) # 走的是方法屬性,就是給request._request.GET重新命名
5. print(request.data) # 走的是方法屬性,值依賴於request._full_data
解析模塊(parser_classes)
只處理請求的數據包參數(request.data)
- form-data
- urlencoded
- application/json
可以針對全局配置、局部配置 方式
全局配置,在項目settings配置文件中配置
# drf框架自定義配置,不需要哪個把哪個注釋掉就可以了。
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
],
}
局部配置,在視圖類中實現:
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
class BookAPIView(APIView):
parser_classes = [JSONParser, FormParser, MultiPartParser]
配置好之后解析模塊的查找順序:
局部(視圖類中)->全局(settings配置文件中)->drf默認配置(APIView中)
源碼分析:
1. 入口:APIVIew的dispatch方法的 request=self.initialize_request(request, *args, **kwargs)
2. 獲取解析類:parsers=self.get_parsers(),
3. 進行局部全局默認配置查找順序進行查找:return [parser() for parser in self.parser_classes]
源碼:
class APIView(View):
# The following policies may be set at either globally, or per-view.
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
def get_parsers(self):
"""
Instantiates and returns the list of parsers that this view can use.
"""
return [parser() for parser in self.parser_classes]
異常模塊(exception_handler)
異常模塊就是在客戶端或者服務端請求的時候出現異常了采取的措施:
我們可以重寫捕獲異常的方法:
源碼分析:
1. 入口:APIVIew的dispatch方法:
except Exception as exc: # 遇到異常捕獲異常
response = self.handle_exception(exc)
2. 執行handle_exception方法:
exception_handler = self.get_exception_handler()
得到django中提供的異常模塊處理方法;
context = self.get_exception_handler_context()
生成一個大字典,包含類名稱,請求參數,不變長參數等等
response = exception_handler(exc, context)
執行函數,傳入大字典響應給客戶端
這里我們看了源碼知道了django中的異常捕獲是通過一個函數方法實現,我們也可以自定義我們的異常處理信息:
在settings中配置:
REST_FRAMEWORK = {
# 異常模塊處理
# 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler', django中提供的
'EXCEPTION_HANDLER': 'api.exception.exception_handler',
}
在api的新建一個exception py文件,文件內寫入:
# 一定要在settings文件中將異常模塊配置自己的異常處理函數
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
def exception_handler(exc, context):
response = drf_exception_handler(exc, context)
detail = '%s - %s - %s' % (context.get('view'), context.get('request').method, exc)
if not response: # 服務端錯誤
response = Response({'detail': detail})
else:
response.data = {'detail': detail}
# 核心:要將response.data.get('detail')信息記錄到日志文件
# logger.waring(response.data.get('detail'))
return response
通過我們自定義的一個異常處理方法最終其他人在用我們的接口的時候,如果接口錯誤會返回以下信息,而不會返回一些亂七八糟的頁面了。
{
"detail": "<api.views.CarAPIView object at 0x000000000D2C6320> - PUT - 方法 “PUT” 不被允許。"
}
響應模塊(Response)
Response,可以對響應的數據指定參數:
- data:響應的數據(data=)
- status:網絡的狀態碼(status=)
- template_name:drf前后台不分離的情況下返回的頁面
- headers:響應頭,一般不寫,走默認的
- exception:異常響應,默認為False
- content_type:默認是application/json的數據格式
from rest_framework.views import APIView
from rest_framework.response import Response
class CarAPIView(APIView):
def get(self,request,*args,**kwargs):
print(request.method)
print(request._request.method)
return Response(data={"msg":"apiview get ok"},status=200)
渲染模塊(render)
可以做全局配置或局部配置,主要針對客戶端返回的數據格式做限制
postman測試工具返回的是json,瀏覽器請求到的結果是一個頁面
from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
# 局部渲染
renderer_classes = [JSONRenderer, BrowsableAPIRenderer]
# 全局渲染配置
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer', # 項目真正上線的時候會把它注釋掉,因為瀏覽器訪問接口是一個頁面
],
}
二次封裝Response類
(自定義一個APIResponse類繼承Response),優化響應
正確的:APIResponse('results'=[]) # 數據狀態碼和狀態信息有默認值,可以不傳
異常的:Response(1,'error', status=400) # 可以按位傳數據狀態碼和狀態信息,錯誤時還可以設置網絡狀態碼
封裝后的響應與封裝前的響應結果一致,但是大大簡化了響應寫法:
class APIResponse(Response):
# 重寫的APIResponse類是繼承了restframework中的Response 再此基礎上重寫多個初始化值
# 設置默認參數
def __init__(self, data_status=0, data_msg='ok', results=None, http_status=None, headers=None, exception=False, **kwargs):
data = {
'status': data_status,
'msg': data_msg,
}
# results可能是False、0等數據,這些數據某些情況下也會作為合法數據返回
if results is not None:
data['results'] = results
data.update(kwargs)
# 繼承並重用父類的 的init方法,把自定義的data數據體傳進去
# 父類的:__init__(self, data=None, status=None,template_name=None, headers=None,exception=False, content_type=None):
# 異常響應,默認為False
super().__init__(data=data, status=http_status, headers=headers, exception=exception)
class CarAPIView(APIView):
def get(self,request,*args,**kwargs):
print(request.query_params)
print(request.data)
# print(request.method)
# print(request._request.method)
return APIResponse(data={"msg":"apiview get ok"})