Django(48)drf請求模組原始碼分析

Silent丿丶黑羽發表於2021-06-07

前言

APIView中的dispatch是整個請求生命過程的核心方法,包含了請求模組,許可權驗證,異常模組和響應模組,我們先來介紹請求模組
 

請求模組:request物件

 

原始碼入口

APIView類中dispatch方法中的:request=self.iniialize_request(*args, **kwargs),原始碼如下:

def initialize_request(self, request, *args, **kwargs):
    """
    Returns the initial request object.
    """
    parser_context = self.get_parser_context(request)

    return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )

 

原始碼分析

原始碼很簡單,第1句parser_context = self.get_parser_context(request),我們進入方法get_parser_context檢視原始碼:

"""
Returns a dict that is passed through to Parser.parse(),
as the `parser_context` keyword argument.
"""
# Note: Additionally `request` and `encoding` will also be added
#       to the context by the Request object.
return {
    'view': self,
    'args': getattr(self, 'args', ()),
    'kwargs': getattr(self, 'kwargs', {})
}

上面的程式碼的意思是:返回一個解析的字典以便於Parser.parse()去解析,另外還通過Request物件新增了上下文requestencoding
 

第二句返回了一個Request物件,點選進入檢視
Django(48)drf請求模組原始碼分析
我們可以分析出,內部對request做了二次封裝,_request是一個HttpRequest物件,並且Request類中還有__getattr__此方法,程式碼如下:

def __getattr__(self, attr):
    """
    If an attribute does not exist on this instance, then we also attempt
    to proxy it to the underlying HttpRequest object.
    """
    try:
        return getattr(self._request, attr)
    except AttributeError:
        return self.__getattribute__(attr)

意思是如果這個例項上不存在一個屬性,那麼我們也會嘗試將其代理到底層HttpRequest物件。接下來我們可以通過案例演示
 

案例演示

Django(48)drf請求模組原始碼分析

我們建立了TestView檢視,檢視函式中列印了3個request屬性,並且在response上打了一個斷點,接下來通過url訪問檢視,進入斷點如下,

我們可以清楚的看到:

  • request是drfRequest物件
  • request下有data屬性,query_params屬性,但是沒有GET屬性

上面還有一個Protected Attributes屬性,裡面包含了_request屬性

我們可以看到_requestWSGIHttpRequest物件,所以它會有GET屬性,所以我們檢視中列印的request.GET實際上和request._request.GET是一樣的,因為request沒有GET屬性,所以它就會訪問_request中的GET屬性,最後我們檢視列印結果,如下:

<QueryDict: {'a': ['1']}>
<QueryDict: {'a': ['1']}>
<QueryDict: {'a': ['1']}>

 

同樣的,POST請求也是如此,我們在檢視中新增POST的請求方式,如下:

def post(self, request, *args, **kwargs):
    print(request.POST)  # 相容
    print(request._request.POST)  # 二次封裝
    print(request.data)  # 擴充,相容性最強,3種請求方式都可以
    return Response("drf post ok")

我們都知道提交資料一般有3種方式

  • multipart/form-data
  • application/x-www-form-urlencoded
  • application/json

首先我們使用multipart/form-data提交請求資料,並請求API

我們檢視pycharm列印結果

<QueryDict: {'a': ['1']}>
<QueryDict: {'a': ['1']}>
<QueryDict: {'a': ['1']}>

可以看到multipart/form-data這種請求方式,都能列印出來
 

接著我們使用application/x-www-form-urlencoded提交請求資料,並請求API

<QueryDict: {'a': ['1']}>
<QueryDict: {'a': ['1']}>
<QueryDict: {'a': ['1']}>

可以看到application/x-www-form-urlencoded這種請求方式,都能列印出來
 

最後我們使用application/json提交請求資料,並請求API
Django(48)drf請求模組原始碼分析
可以看到application/json這種請求方式,只有request.data能列印出來

<QueryDict: {}>
<QueryDict: {}>
{'a': 1}

所以request.data相容性最強
 

總結

  1. drfrequest進行了二次封裝,request._request就是原生的WSGIRequest
  2. 原生request的屬性和方法都可以被drfrequest物件直接訪問(相容)
  3. drf請求的所有url拼接引數均被解析到query_params中,所有的資料包均被解析到data
  4. 其中post請求,request.data的相容性最強,能相容前臺傳輸的json格式的資料

相關文章