国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

flask 源碼解析:請(qǐng)求

weizx / 3325人閱讀

摘要:可以看到,雖然是同樣的請(qǐng)求數(shù)據(jù),在不同的階段和不同組件看來(lái),是完全不同的形式。請(qǐng)求還有一個(gè)不那么明顯的特性它不能被應(yīng)用修改,應(yīng)用只能讀取請(qǐng)求的數(shù)據(jù)。

這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表:

flask 源碼解析:簡(jiǎn)介

flask 源碼解析:應(yīng)用啟動(dòng)流程

flask 源碼解析:路由

flask 源碼解析:上下文

flask 源碼解析:請(qǐng)求

flask 源碼解析:響應(yīng)

簡(jiǎn)介

對(duì)于物理鏈路來(lái)說(shuō),請(qǐng)求只是不同電壓信號(hào),它根本不知道也不需要知道請(qǐng)求格式和內(nèi)容到底是怎樣的;
對(duì)于 TCP 層來(lái)說(shuō),請(qǐng)求就是傳輸?shù)臄?shù)據(jù)(二進(jìn)制的數(shù)據(jù)流),它只要發(fā)送給對(duì)應(yīng)的應(yīng)用程序就行了;
對(duì)于 HTTP 層的服務(wù)器來(lái)說(shuō),請(qǐng)求必須是符合 HTTP 協(xié)議的內(nèi)容;
對(duì)于 WSGI server 來(lái)說(shuō),請(qǐng)求又變成了文件流,它要讀取其中的內(nèi)容,把 HTTP 請(qǐng)求包含的各種信息保存到一個(gè)字典中,調(diào)用 WSGI app;
對(duì)于 flask app 來(lái)說(shuō),請(qǐng)求就是一個(gè)對(duì)象,當(dāng)需要某些信息的時(shí)候,只需要讀取該對(duì)象的屬性或者方法就行了。

可以看到,雖然是同樣的請(qǐng)求數(shù)據(jù),在不同的階段和不同組件看來(lái),是完全不同的形式。因?yàn)槊總€(gè)組件都有它本身的目的和功能,這和生活中的事情一個(gè)道理:對(duì)于同樣的事情,不同的人或者同一個(gè)人不同人生階段的理解是不一樣的。

這篇文章呢,我們只考慮最后一個(gè)內(nèi)容,flask 怎么看待請(qǐng)求。

請(qǐng)求

我們知道要訪問(wèn) flask 的請(qǐng)求對(duì)象非常簡(jiǎn)單,只需要 from flask import request

from flask import request

with app.request_context(environ):
    assert request.method == "POST"

前面一篇文章 已經(jīng)介紹了這個(gè)神奇的變量是怎么工作的,它最后對(duì)應(yīng)了 flask.wrappers:Request 類的對(duì)象。
這個(gè)類內(nèi)部的實(shí)現(xiàn)雖然我們還不清楚,但是我們知道它接受 WSGI server 傳遞過(guò)來(lái)的 environ 字典變量,并提供了很多常用的屬性和方法可以使用,比如請(qǐng)求的 method、path、args 等。
請(qǐng)求還有一個(gè)不那么明顯的特性——它不能被應(yīng)用修改,應(yīng)用只能讀取請(qǐng)求的數(shù)據(jù)。

這個(gè)類的定義很簡(jiǎn)單,它繼承了 werkzeug.wrappers:Request,然后添加了一些屬性,這些屬性和 flask 的邏輯有關(guān),比如 view_args、blueprint、json 處理等。它的代碼如下:

from werkzeug.wrappers import Request as RequestBase


class Request(RequestBase):
    """
    The request object is a :class:`~werkzeug.wrappers.Request` subclass and
    provides all of the attributes Werkzeug defines plus a few Flask
    specific ones.
    """

    #: The internal URL rule that matched the request.  This can be
    #: useful to inspect which methods are allowed for the URL from
    #: a before/after handler (``request.url_rule.methods``) etc.
    url_rule = None

    #: A dict of view arguments that matched the request.  If an exception
    #: happened when matching, this will be ``None``.
    view_args = None

    @property
    def max_content_length(self):
        """Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""
        ctx = _request_ctx_stack.top
        if ctx is not None:
            return ctx.app.config["MAX_CONTENT_LENGTH"]

    @property
    def endpoint(self):
        """The endpoint that matched the request.  This in combination with
        :attr:`view_args` can be used to reconstruct the same or a
        modified URL.  If an exception happened when matching, this will
        be ``None``.
        """
        if self.url_rule is not None:
            return self.url_rule.endpoint

    @property
    def blueprint(self):
        """The name of the current blueprint"""
        if self.url_rule and "." in self.url_rule.endpoint:
            return self.url_rule.endpoint.rsplit(".", 1)[0]

    @property
    def is_json(self):
        mt = self.mimetype
        if mt == "application/json":
            return True
        if mt.startswith("application/") and mt.endswith("+json"):
            return True
        return False

這段代碼沒(méi)有什難理解的地方,唯一需要說(shuō)明的就是 @property 裝飾符能夠把類的方法變成屬性,這是 python 中經(jīng)常見到的用法。

接著我們就要看 werkzeug.wrappers:Request

class Request(BaseRequest, AcceptMixin, ETagRequestMixin,
              UserAgentMixin, AuthorizationMixin,
              CommonRequestDescriptorsMixin):

    """Full featured request object implementing the following mixins:

    - :class:`AcceptMixin` for accept header parsing
    - :class:`ETagRequestMixin` for etag and cache control handling
    - :class:`UserAgentMixin` for user agent introspection
    - :class:`AuthorizationMixin` for http auth handling
    - :class:`CommonRequestDescriptorsMixin` for common headers
    """

這個(gè)方法有一點(diǎn)比較特殊,它沒(méi)有任何的 body。但是有多個(gè)基類,第一個(gè)是 BaseRequest,其他的都是各種 Mixin
這里要講一下 Mixin 機(jī)制,這是 python 多繼承的一種方式,如果你希望某個(gè)類可以自行組合它的特性(比如這里的情況),或者希望某個(gè)特性用在多個(gè)類中,就可以使用 Mixin。
如果我們只需要能處理各種 Accept 頭部的請(qǐng)求,可以這樣做:

class Request(BaseRequest, AcceptMixin)
    pass

但是不要濫用 Mixin,在大多數(shù)情況下子類繼承了父類,然后實(shí)現(xiàn)需要的邏輯就能滿足需求。

我們先來(lái)看看 BaseRequest:

class BaseRequest(object):
    def __init__(self, environ, populate_request=True, shallow=False):
        self.environ = environ
        if populate_request and not shallow:
            self.environ["werkzeug.request"] = self
        self.shallow = shallow

能看到實(shí)例化需要的唯一變量是 environ,它只是簡(jiǎn)單地把變量保存下來(lái),并沒(méi)有做進(jìn)一步的處理。Request 的內(nèi)容很多,其中相當(dāng)一部分是被 @cached_property 裝飾的方法,比如下面這種:

    @cached_property
    def args(self):
        """The parsed URL parameters."""
        return url_decode(wsgi_get_bytes(self.environ.get("QUERY_STRING", "")),
                          self.url_charset, errors=self.encoding_errors,
                          cls=self.parameter_storage_class)

    @cached_property
    def stream(self):
        """The stream to read incoming data from.  Unlike :attr:`input_stream`
        this stream is properly guarded that you can"t accidentally read past
        the length of the input.  Werkzeug will internally always refer to
        this stream to read data which makes it possible to wrap this
        object with a stream that does filtering.
        """
        _assert_not_shallow(self)
        return get_input_stream(self.environ)

    @cached_property
    def form(self):
        """The form parameters."""
        self._load_form_data()
        return self.form

    @cached_property
    def cookies(self):
        """Read only access to the retrieved cookie values as dictionary."""
        return parse_cookie(self.environ, self.charset,
                            self.encoding_errors,
                            cls=self.dict_storage_class)

    @cached_property
    def headers(self):
        """The headers from the WSGI environ as immutable
        :class:`~werkzeug.datastructures.EnvironHeaders`.
        """
        return EnvironHeaders(self.environ)

@cached_property 從名字就能看出來(lái),它是 @property 的升級(jí)版,添加了緩存功能。我們知道
@property 能把某個(gè)方法轉(zhuǎn)換成屬性,每次訪問(wèn)屬性的時(shí)候,它都會(huì)執(zhí)行底層的方法作為結(jié)果返回。
@cached_property 也一樣,區(qū)別是只有第一次訪問(wèn)的時(shí)候才會(huì)調(diào)用底層的方法,后續(xù)的方法會(huì)直接使用之前返回的值。
那么它是如何實(shí)現(xiàn)的呢?我們能在 werkzeug.utils 找到它的定義:

class cached_property(property):

    """A decorator that converts a function into a lazy property.  The
    function wrapped is called the first time to retrieve the result
    and then that calculated result is used the next time you access
    the value.

    The class has to have a `__dict__` in order for this property to
    work.
    """

    # implementation detail: A subclass of python"s builtin property
    # decorator, we override __get__ to check for a cached value. If one
    # choses to invoke __get__ by hand the property will still work as
    # expected because the lookup logic is replicated in __get__ for
    # manual invocation.

    def __init__(self, func, name=None, doc=None):
        self.__name__ = name or func.__name__
        self.__module__ = func.__module__
        self.__doc__ = doc or func.__doc__
        self.func = func

    def __set__(self, obj, value):
        obj.__dict__[self.__name__] = value

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        value = obj.__dict__.get(self.__name__, _missing)
        if value is _missing:
            value = self.func(obj)
            obj.__dict__[self.__name__] = value
        return value

這個(gè)裝飾器同時(shí)也是實(shí)現(xiàn)了 __set____get__ 方法的描述器。
訪問(wèn)它裝飾的屬性,就會(huì)調(diào)用 __get__ 方法,這個(gè)方法先在 obj.__dict__ 中尋找是否已經(jīng)存在對(duì)應(yīng)的值。如果存在,就直接返回;如果不存在,調(diào)用底層的函數(shù)
self.func,并把得到的值保存起來(lái),再返回。這也是它能實(shí)現(xiàn)緩存的原因:因?yàn)樗鼤?huì)把函數(shù)的值作為屬性保存到對(duì)象中。

關(guān)于 Request 內(nèi)部各種屬性的實(shí)現(xiàn),就不分析了,因?yàn)樗鼈兠總€(gè)具體的實(shí)現(xiàn)都不太一樣,也不復(fù)雜,無(wú)外乎對(duì) environ 字典中某些字段做一些處理和計(jì)算。
接下來(lái)回過(guò)頭來(lái)看看 Mixin,這里只用 AcceptMixin 作為例子:

class AcceptMixin(object):

    @cached_property
    def accept_mimetypes(self):
        return parse_accept_header(self.environ.get("HTTP_ACCEPT"), MIMEAccept)

    @cached_property
    def accept_charsets(self):
        return parse_accept_header(self.environ.get("HTTP_ACCEPT_CHARSET"),
                                   CharsetAccept)

    @cached_property
    def accept_encodings(self):
        return parse_accept_header(self.environ.get("HTTP_ACCEPT_ENCODING"))

    @cached_property
    def accept_languages(self):
        return parse_accept_header(self.environ.get("HTTP_ACCEPT_LANGUAGE"),
                                   LanguageAccept)

AcceptMixin 實(shí)現(xiàn)了請(qǐng)求內(nèi)容協(xié)商的部分,比如請(qǐng)求接受的語(yǔ)言、編碼格式、相應(yīng)內(nèi)容等。
它也是定義了很多 @cached_property 方法,雖然自己沒(méi)有 __init__ 方法,但是也直接使用了
self.environ,因此它并不能直接使用,只能和 BaseRequest 一起出現(xiàn)。

參考資料

Flask official docs

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/38451.html

相關(guān)文章

  • flask 源碼解析:響應(yīng)

    摘要:我們知道響應(yīng)分為三個(gè)部分狀態(tài)欄版本狀態(tài)碼和說(shuō)明頭部以冒號(hào)隔開的字符對(duì),用于各種控制和協(xié)商服務(wù)端返回的數(shù)據(jù)。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡(jiǎn)介 flask 源碼解析:應(yīng)用啟動(dòng)流程 flask 源碼解析:路由 flask 源碼解析:上下文 flask 源碼解析:請(qǐng)求 flask 源碼解析:響應(yīng) response 簡(jiǎn)介 在 f...

    wslongchen 評(píng)論0 收藏0
  • flask 源碼解析:應(yīng)用啟動(dòng)流程

    摘要:中有一個(gè)非常重要的概念每個(gè)應(yīng)用都是一個(gè)可調(diào)用的對(duì)象。它規(guī)定了的接口,會(huì)調(diào)用,并傳給它兩個(gè)參數(shù)包含了請(qǐng)求的所有信息,是處理完之后需要調(diào)用的函數(shù),參數(shù)是狀態(tài)碼響應(yīng)頭部還有錯(cuò)誤信息。一般來(lái)說(shuō),嵌套的最后一層是業(yè)務(wù)應(yīng)用,中間就是。 文章屬于作者原創(chuàng),原文發(fā)布在個(gè)人博客。 WSGI 所有的 python web 框架都要遵循 WSGI 協(xié)議,如果對(duì) WSGI 不清楚,可以查看我之前的介紹文章。 ...

    whatsns 評(píng)論0 收藏0
  • flask 源碼解析:上下文

    摘要:但是這些對(duì)象和全局變量不同的是它們必須是動(dòng)態(tài)的,因?yàn)樵诙嗑€程或者多協(xié)程的情況下,每個(gè)線程或者協(xié)程獲取的都是自己獨(dú)特的對(duì)象,不會(huì)互相干擾。中有兩種上下文和。就是實(shí)現(xiàn)了類似的效果多線程或者多協(xié)程情況下全局變量的隔離效果。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡(jiǎn)介 flask 源碼解析:應(yīng)用啟動(dòng)流程 flask 源碼解析:路由 flas...

    Labradors 評(píng)論0 收藏0
  • flask 源碼解析:簡(jiǎn)介

    摘要:簡(jiǎn)介官網(wǎng)上對(duì)它的定位是一個(gè)微開發(fā)框架。另外一個(gè)必須理解的概念是,簡(jiǎn)單來(lái)說(shuō)就是一套和框架應(yīng)用之間的協(xié)議。功能比較豐富,支持解析自動(dòng)防止攻擊繼承變量過(guò)濾器流程邏輯支持代碼邏輯集成等等。那么,從下一篇文章,我們就正式開始源碼之旅了 文章屬于作者原創(chuàng),原文發(fā)布在個(gè)人博客。 flask 簡(jiǎn)介 Flask 官網(wǎng)上對(duì)它的定位是一個(gè)微 python web 開發(fā)框架。 Flask is a micro...

    megatron 評(píng)論0 收藏0
  • flask route設(shè)計(jì)思路

    摘要:引言本文主要梳理了源碼中的設(shè)計(jì)思路。協(xié)議將處理請(qǐng)求的組件按照功能及調(diào)用關(guān)系分成了三種。不論是什么,最終都會(huì)調(diào)用函數(shù)。 引言 本文主要梳理了flask源碼中route的設(shè)計(jì)思路。首先,從WSGI協(xié)議的角度介紹flask route的作用;其次,詳細(xì)講解如何借助werkzeug庫(kù)的Map、Rule實(shí)現(xiàn)route;最后,梳理了一次完整的http請(qǐng)求中route的完整流程。 flask rou...

    vvpale 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<