flask0.1源碼

-- coding: utf-8 --

"""
Flask-Origin
~~~~~~~~~~~~~

Flask 0.1版本源碼注解。
:copyright: (c) 2018 Grey Li
:license: MIT, see LICENSE for more details.

"""
from future import with_statement
import os
import sys

from jinja2 import Environment, PackageLoader, FileSystemLoader
from werkzeug import Request as RequestBase, Response as ResponseBase,
LocalStack, LocalProxy, create_environ, SharedDataMiddleware
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException
from werkzeug.contrib.securecookie import SecureCookie

這些從Werkzeug和Jinja2導(dǎo)入的輔助函數(shù)(utilities)沒有在

模塊內(nèi)使用,而是直接作為外部接口開放

from werkzeug import abort, redirect
from jinja2 import Markup, escape

優(yōu)先使用pkg_resource哗蜈,如果無法工作則使用cwd闻牡。

try:
import pkg_resources
pkg_resources.resource_stream
except (ImportError, AttributeError):
pkg_resources = None

class Request(RequestBase):
"""Flask默認(rèn)使用的請(qǐng)求對(duì)象孵稽,用來記住匹配的端點(diǎn)值(endpoint)和視圖參數(shù)(view arguments)嗡善。
這就是最終的flask.request對(duì)象篡腌。如果你想替換掉這個(gè)請(qǐng)求對(duì)象碱璃,可以子類化這個(gè)
類弄痹,然后將你的子類賦值給flask.Flask.request_class。
"""

def __init__(self, environ):
    RequestBase.__init__(self, environ)
    self.endpoint = None  # 當(dāng)前請(qǐng)求的端點(diǎn)
    self.view_args = None  # 當(dāng)前請(qǐng)求的視圖參數(shù)嵌器,會(huì)作為關(guān)鍵字參數(shù)傳入視圖函數(shù)

class Response(ResponseBase):
"""Flask默認(rèn)使用的響應(yīng)對(duì)象肛真。除了將MIME類型默認(rèn)設(shè)置為HTML外,和Werkzeug提供的響應(yīng)對(duì)象
完全相同爽航。通常情況下蚓让,你不需要自己創(chuàng)建這個(gè)對(duì)象,因?yàn)閒lask.Flask.make_response
會(huì)負(fù)責(zé)這個(gè)工作讥珍。
如果你想替換這個(gè)響應(yīng)對(duì)象历极,你可以子類化這個(gè)類,然后將你的子類賦值給flask.Flask.response_class串述。
"""
default_mimetype = 'text/html'

class _RequestGlobals(object):
pass

class _RequestContext(object):
"""請(qǐng)求上下文(request context)包含所有請(qǐng)求相關(guān)的信息执解。它會(huì)在請(qǐng)求進(jìn)入時(shí)被創(chuàng)建,
然后被推送到_request_ctx_stack,在請(qǐng)求結(jié)束時(shí)會(huì)被相應(yīng)的移除衰腌。它會(huì)為提供的
WSGI環(huán)境創(chuàng)建URL適配器(adapter)和請(qǐng)求對(duì)象新蟆。
"""
# 會(huì)在flask.Flask.request_context和flask.Flask.test_requset_context方法中
# 調(diào)用,以便生成請(qǐng)求上下文右蕊。
def init(self, app, environ):
self.app = app
self.url_adapter = app.url_map.bind_to_environ(environ) # 綁定了當(dāng)前環(huán)境信息琼稻,用于構(gòu)建URL,在url_for函數(shù)中使用
self.request = app.request_class(environ) # 創(chuàng)建請(qǐng)求對(duì)象饶囚,包含請(qǐng)求信息
self.session = app.open_session(self.request) # 創(chuàng)建session對(duì)象帕翻,用于存儲(chǔ)用戶會(huì)話數(shù)據(jù)到cookie中
self.g = _RequestGlobals() # 創(chuàng)建g對(duì)象,用于在當(dāng)前請(qǐng)求存儲(chǔ)全局變量
self.flashes = None # 存儲(chǔ)當(dāng)前請(qǐng)求的通過flash函數(shù)發(fā)送的消息

def __enter__(self):
    _request_ctx_stack.push(self)  # 將當(dāng)前請(qǐng)求上下文對(duì)象推送到_request_ctx_stack堆棧萝风,這個(gè)堆棧在最后定義

def __exit__(self, exc_type, exc_value, tb):
    # 在調(diào)試模式(debug mode)而且有異常發(fā)生時(shí)嘀掸,不要移除(pop)請(qǐng)求堆棧。
    # 這將允許調(diào)試器(debugger)在交互式shell中仍然可以獲取請(qǐng)求對(duì)象规惰。
    if tb is None or not self.app.debug:
        _request_ctx_stack.pop()

def url_for(endpoint, **values):
"""根據(jù)給定的端點(diǎn)和提供的方法生成一個(gè)URL睬塌。
對(duì)于目標(biāo)端點(diǎn)未知的變量參數(shù),將會(huì)作為查詢參數(shù)附加在URL后面(生成查詢字符串)歇万。
:param endpoint: URL的端點(diǎn)值(函數(shù)名)揩晴。
:param values: URL規(guī)則的變量參數(shù)。
"""
return _request_ctx_stack.top.url_adapter.build(endpoint, values) # 這里堆棧的棧頂(top)即上面的請(qǐng)求上下文對(duì)象實(shí)例

def flash(message):
"""閃現(xiàn)(flash)一個(gè)消息到下一個(gè)請(qǐng)求贪磺。為了從session中移除閃現(xiàn)過的消息
并將其顯示給用戶硫兰,你必須在模板中調(diào)用get_flashed_messages。
:param message: 被閃現(xiàn)的消息寒锚。
"""
session['_flashes'] = (session.get('_flashes', [])) + [message]

def get_flashed_messages():
"""從session里拉冉儆场(pull)所有要閃現(xiàn)的消息并返回它們。在同一個(gè)請(qǐng)求中對(duì)這個(gè)函數(shù)的
進(jìn)一步調(diào)用會(huì)返回同樣的消息刹前。
"""
flashes = _request_ctx_stack.top.flashes
if flashes is None:
_request_ctx_stack.top.flashes = flashes =
session.pop('_flashes', [])
return flashes

def render_template(template_name, **context):
"""使用給定的上下文從模板(template)文件夾渲染一個(gè)模板苏研。

:param template_name: 要被渲染的模板文件名。
:param context: 在模板上下文中應(yīng)該可用(available)的變量腮郊。
"""
current_app.update_template_context(context)
return current_app.jinja_env.get_template(template_name).render(context)

def render_template_string(source, **context):
"""使用給定的模板源代碼字符串(source string)和上下文渲染一個(gè)模板。
:param template_name: 要被渲染的模板源代碼筹燕。
:param context: 在模板上下文中應(yīng)該可用的變量轧飞。
"""
current_app.update_template_context(context)
return current_app.jinja_env.from_string(source).render(context)

def _default_template_ctx_processor():
"""默認(rèn)的模板上下文處理器(processor)。注入request撒踪、session和g过咬。"""
# 把request、session和g注入到模板上下文制妄,以便可以直接在模板中使用這些變量掸绞。
reqctx = _request_ctx_stack.top
return dict(
request=reqctx.request,
session=reqctx.session,
g=reqctx.g
)

def _get_package_path(name):
"""返回包的路徑,如果找不到則返回當(dāng)前工作目錄(cwd)。"""
try:
return os.path.abspath(os.path.dirname(sys.modules[name].file))
except (KeyError, AttributeError):
return os.getcwd()

class Flask(object):
"""這個(gè)flask對(duì)象實(shí)現(xiàn)了WSGI程序并作為中心對(duì)象存在衔掸。傳入的參數(shù)(package_name)為
程序所在的模塊或包的名稱烫幕。一旦這個(gè)對(duì)象被創(chuàng)建,它將作為一個(gè)中心注冊處敞映,所有的視圖
函數(shù)较曼、URL規(guī)則、模板配置等等都將注冊到這里振愿。
包的名稱被用來從包的內(nèi)部或模塊所在的文件夾解析資源捷犹,具體的位置取決于傳入的包名稱
參數(shù)(package_name)指向一個(gè)真實(shí)的Python包(包含init.py文件的文件夾)
還是一個(gè)標(biāo)準(zhǔn)的模塊(.py文件)。
關(guān)于資源加載的更多信息冕末,參見open_resource萍歉。
通常,你會(huì)在你的主腳本或包中的init.py文件里使用下面的方式創(chuàng)建一個(gè)Flask實(shí)例:
from flask import Flask
app = Flask(name)

"""

#: 用作請(qǐng)求對(duì)象的類档桃。更多信息參見flask.Request枪孩。
request_class = Request

#: 用作響應(yīng)對(duì)象的類蜂桶。更多信息參見flask.Response昂儒。
response_class = Response

#: 靜態(tài)文件的路徑友扰。如果你不想使用靜態(tài)文件腮出,可以將這個(gè)值設(shè)為None用僧,這樣不會(huì)添加
#: 相應(yīng)的URL規(guī)則贿堰,而且開發(fā)服務(wù)器將不再提供(serve)任何靜態(tài)文件愚战。
static_path = '/static'

#: 如果設(shè)置了密鑰(secret key)宗兼,加密組件可以使用它來為
#: cookies或其他東西簽名抚垄。比如蜕窿,當(dāng)你想使用安全的cookie時(shí),把它設(shè)為一個(gè)復(fù)雜的隨機(jī)值呆馁。
secret_key = None

#: 安全cookie使用這個(gè)值作為session cookie的名稱桐经。
session_cookie_name = 'session'  # 存儲(chǔ)session對(duì)象數(shù)據(jù)的cookie名稱

#: 直接傳入Jinja2環(huán)境的選項(xiàng)。
jinja_options = dict(
    autoescape=True,  # 默認(rèn)開啟自動(dòng)轉(zhuǎn)義浙滤,即轉(zhuǎn)義不安全字符為HTML實(shí)體阴挣,比如“>”、“<”等纺腊。
    extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
)

def __init__(self, package_name):
    #: 調(diào)試標(biāo)志畔咧。將它設(shè)為True來開啟調(diào)試模式。在調(diào)試模式下揖膜,當(dāng)一個(gè)未捕捉
    #: 的異常觸發(fā)時(shí)誓沸,調(diào)試器會(huì)啟動(dòng);而且壹粟,當(dāng)代碼中的變動(dòng)被探測到時(shí)拜隧,開發(fā)
    #: 服務(wù)器會(huì)自動(dòng)重載程序。
    self.debug = False

    #: 包或模塊的名稱。一旦它通過構(gòu)造器設(shè)置后洪添,就不要更改這個(gè)值垦页。
    self.package_name = package_name

    #: 定位程序的根目錄。
    self.root_path = _get_package_path(self.package_name)

    ###################################
    # 下面是幾個(gè)存儲(chǔ)回調(diào)函數(shù)的字典或列表
    ###################################

    #: 一個(gè)儲(chǔ)存所有已注冊的視圖函數(shù)的字典薇组。字典的鍵將是函數(shù)的名稱外臂,這些名稱
    #: 也被用來生成URL;字典的值是函數(shù)對(duì)象本身律胀。
    #: 要注冊一個(gè)視圖函數(shù)宋光,使用route裝飾器(decorator)。
    self.view_functions = {}

    #: 一個(gè)儲(chǔ)存所有已注冊的錯(cuò)誤處理器的字典炭菌。字段的鍵是整型(integer)類型的
    #: 錯(cuò)誤碼罪佳,字典的值是處理對(duì)應(yīng)錯(cuò)誤的函數(shù)。
    #: 要注冊一個(gè)錯(cuò)誤處理器黑低,使用errorhandler裝飾器赘艳。
    self.error_handlers = {}

    #: 一個(gè)應(yīng)該在請(qǐng)求開始進(jìn)入時(shí)、請(qǐng)求分發(fā)開始前調(diào)用的函數(shù)列表克握。舉例來說蕾管,
    #: 這可以用來打開數(shù)據(jù)庫連接或獲取當(dāng)前登錄的用戶。
    #: 要注冊一個(gè)函數(shù)到這里菩暗,使用before_request裝飾器掰曾。
    self.before_request_funcs = []

    #: 一個(gè)應(yīng)該在請(qǐng)求處理結(jié)束時(shí)調(diào)用的函數(shù)列表。這些函數(shù)會(huì)被傳入當(dāng)前的響應(yīng)
    #: 對(duì)象停团,你可以在函數(shù)內(nèi)修改或替換它旷坦。
    #: 要注冊一個(gè)函數(shù)到這里,使用after_request裝飾器佑稠。
    self.after_request_funcs = []

    #: 一個(gè)將被無參數(shù)調(diào)用以生成模板上下文的的函數(shù)列表秒梅。每一個(gè)函數(shù)應(yīng)返回一個(gè)
    #: 用于更新模板上下文的字典。
    #: 要注冊一個(gè)函數(shù)到這里舌胶,使用context_processor裝飾器捆蜀。
    self.template_context_processors = [_default_template_ctx_processor]  # 默認(rèn)的處理器用來注入session、request和g

    self.url_map = Map()

    if self.static_path is not None:
        self.url_map.add(Rule(self.static_path + '/<filename>',
                              build_only=True, endpoint='static'))
        if pkg_resources is not None:
            target = (self.package_name, 'static')
        else:
            target = os.path.join(self.root_path, 'static')
        self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {  # SharedDataMiddleware中間件用來為程序添加處理靜態(tài)文件的能力
            self.static_path: target  # URL路徑和實(shí)際文件目錄(static文件夾)的映射
        })

    #: Jinja2環(huán)境幔嫂。它通過jinja_options創(chuàng)建漱办,加載器(loader)通過
    #: create_jinja_loader函數(shù)返回。
    self.jinja_env = Environment(loader=self.create_jinja_loader(),
                                 **self.jinja_options)
    self.jinja_env.globals.update(  # 將url_for和get_flashed_messages函數(shù)作為全局對(duì)象注入到模板上下文婉烟,以便在模板中調(diào)用
        url_for=url_for,
        get_flashed_messages=get_flashed_messages
    )

def create_jinja_loader(self):
    """創(chuàng)建Jinja加載器。默認(rèn)只是返回一個(gè)對(duì)應(yīng)配置好的包的包加載器暇屋,它會(huì)從
    templates文件夾中尋找模板似袁。要添加其他加載器,可以重載這個(gè)方法。
    """
    if pkg_resources is None:
        return FileSystemLoader(os.path.join(self.root_path, 'templates'))
    return PackageLoader(self.package_name)

def update_template_context(self, context):
    """使用常用的變量更新模板上下文昙衅。這會(huì)注入request扬霜、session和g到模板上下文中。
    :param context: 包含額外添加的變量的字典而涉,用來更新上下文著瓶。
    """
    reqctx = _request_ctx_stack.top
    for func in self.template_context_processors:  # 調(diào)用所有使用context_processor裝飾器注冊的模板上下文處理函數(shù),更新模板上下文
        context.update(func())

def run(self, host='localhost', port=5000, **options):
    """在本地開發(fā)服務(wù)器上運(yùn)行程序啼县。如果debug標(biāo)志被設(shè)置材原,這個(gè)服務(wù)器
    會(huì)在代碼更改時(shí)自動(dòng)重載,并會(huì)在異常發(fā)生時(shí)顯示一個(gè)調(diào)試器季眷。
    
    :param host: 監(jiān)聽的主機(jī)名余蟹。設(shè)為'0.0.0.0'可以讓服務(wù)器外部可見。
    :param port: 服務(wù)器的端口子刮。
    :param options: 這些選項(xiàng)將被轉(zhuǎn)發(fā)給底層的Werkzeug服務(wù)器威酒。更多信息
                    參見werkzeug.run_simple。
    """
    from werkzeug import run_simple
    if 'debug' in options:
        self.debug = options.pop('debug')
    options.setdefault('use_reloader', self.debug)  # 如果debug為True挺峡,開啟重載器(reloader)
    options.setdefault('use_debugger', self.debug)  # 如果debug為True葵孤,開啟調(diào)試器(debugger)
    return run_simple(host, port, self, **options)

def test_client(self):
    """為這個(gè)程序創(chuàng)建一個(gè)測試客戶端。"""
    from werkzeug import Client
    return Client(self, self.response_class, use_cookies=True)

def open_resource(self, resource):
    """從程序的資源文件夾打開一個(gè)資源橱赠。至于它是如何工作的尤仍,考慮下面的文件
    目錄:
        /myapplication.py
        /schemal.sql
        /static
            /style.css
        /templates
            /layout.html
            /index.html
    如果你想打開schema.sql文件,可以這樣做:
        with app.open_resource('schema.sql') as f:
            contents = f.read()
            do_something_with(contents)
    :param resource: 資源文件的名稱病线。要獲取子文件夾中的資源吓著,使用斜線作為分界符。
    """
    if pkg_resources is None:
        return open(os.path.join(self.root_path, resource), 'rb')
    return pkg_resources.resource_stream(self.package_name, resource)

def open_session(self, request):
    """創(chuàng)建或打開一個(gè)新的session送挑。默認(rèn)的實(shí)現(xiàn)是存儲(chǔ)所有的用戶會(huì)話(session)
    數(shù)據(jù)到一個(gè)簽名的cookie中绑莺。這需要secret_key屬性被設(shè)置。
    :param request: request_class的實(shí)例惕耕。
    """
    key = self.secret_key
    if key is not None:
        return SecureCookie.load_cookie(request, self.session_cookie_name,
                                        secret_key=key)

def save_session(self, session, response):
    """如果需要更新纺裁,保存session。默認(rèn)實(shí)現(xiàn)參見open_session司澎。
    
    :param session: 要被保存的session
                    (一個(gè)werkzeug.contrib.securecookie.SecureCookie對(duì)象)
    :param response: 一個(gè)response_class實(shí)例欺缘。
    """
    if session is not None:
        session.save_cookie(response, self.session_cookie_name)

def add_url_rule(self, rule, endpoint, **options):
    """連接一個(gè)URL規(guī)則。效果和route裝飾器完全相同挤安,不過不會(huì)為端點(diǎn)注冊視圖函數(shù)谚殊。
    基本示例:
        @app.route('/')
        def index():
            pass
    和下面相同:
        def index():
            pass
        app.add_url_rule('/', 'index')
        app.view_functions['index'] = index
    :param rule: 字符串形式的URL規(guī)則。
    :param endpoint: 對(duì)應(yīng)被注冊的URL規(guī)則的端點(diǎn)蛤铜。Flask默認(rèn)將視圖函數(shù)名作為端點(diǎn)嫩絮。
    :param options: 轉(zhuǎn)發(fā)給底層的werkzeug.routing.Rule對(duì)象的選項(xiàng)丛肢。
    """
    options['endpoint'] = endpoint
    options.setdefault('methods', ('GET',))  #  默認(rèn)監(jiān)聽GET方法
    self.url_map.add(Rule(rule, **options))

################################################################################
# 下面是幾個(gè)用于注冊各類回調(diào)函數(shù)的裝飾器,函數(shù)對(duì)象存儲(chǔ)到上面創(chuàng)建的幾個(gè)字典和列表屬性中  
################################################################################

def route(self, rule, **options):
    """一個(gè)用于為給定的URL規(guī)則注冊視圖函數(shù)的裝飾器剿干。示例:
        @app.route('/')
        def index():
            return 'Hello World'
    路由中的變量部分可以使用尖括號(hào)來指定(/user/<username>)蜂怎。默認(rèn)情況下,
    URL中的變量部分接受任意不包含斜線的字符串置尔,你也可以使用<converter:name>
    的形式來指定一個(gè)不同的轉(zhuǎn)換器杠步。
    變量部分將被作為關(guān)鍵字參數(shù)傳入視圖函數(shù)。
    可用的轉(zhuǎn)換器如下所示:
    ========= =======================================
    int       接受整型
    float     類似int榜轿,但是接受浮點(diǎn)數(shù)(floating point)
    path      類似默認(rèn)值幽歼,但接受斜線
    ========= =======================================
    下面是一些示例:
        @app.route('/')
        def index():
            pass
        @app.route('/<username>')
        def show_user(username):
            pass
        @app.route('/post/<int:post_id>')
        def show_post(post_id):
            pass
    一個(gè)重要的細(xì)節(jié)是留意Flask是如何處理斜線的。為了讓每一個(gè)URL獨(dú)一無二差导,
    下面的規(guī)則被應(yīng)用:
    1. 如果一個(gè)規(guī)則以一個(gè)斜線結(jié)尾而用戶請(qǐng)求的地址不包含斜線试躏,那么該用戶
    會(huì)被重定向到相同的頁面并附加一個(gè)結(jié)尾斜線。
    2. 如果一個(gè)規(guī)則沒有以斜線結(jié)尾而用戶請(qǐng)求的頁面包含了一個(gè)結(jié)尾斜線设褐,
    會(huì)拋出一個(gè)404錯(cuò)誤颠蕴。
    
    這和Web服務(wù)器處理靜態(tài)文件的方式相一致。這也可以讓你安全的使用相對(duì)鏈接目標(biāo)助析。
    這個(gè)route裝飾器也接受一系列參數(shù):
    :param rule: 字符串形式的URL規(guī)則
    :param methods: 一個(gè)方法列表犀被,可用的值限定為(GET、POST等)外冀。默認(rèn)一個(gè)
                    規(guī)則僅監(jiān)聽GET(以及隱式的HEAD)
    :param subdomain: 當(dāng)子域名匹配使用時(shí)寡键,為規(guī)則指定子域。
    :param strict_slashes: 可以用來為這個(gè)規(guī)則關(guān)閉嚴(yán)格的斜線設(shè)置雪隧,見上西轩。
    :param options: 轉(zhuǎn)發(fā)到底層的werkzeug.routing.Rule對(duì)象的其他選項(xiàng)。
    """
    def decorator(f):
        self.add_url_rule(rule, f.__name__, **options)
        self.view_functions[f.__name__] = f  # 將端點(diǎn)(默認(rèn)使用函數(shù)名脑沿,即f.__name__)和函數(shù)對(duì)象的映射存儲(chǔ)到view_functions字典
        return f
    return decorator

def errorhandler(self, code):
    """一個(gè)用于為給定的錯(cuò)誤碼注冊函數(shù)的裝飾器藕畔。示例:
        @app.errorhandler(404)
        def page_not_found(error):
            return 'This page does not exist', 404
    你也可以不使用errorhandler注冊一個(gè)函數(shù)作為錯(cuò)誤處理器。下面的例子同上:
        def page_not_found(error):
            return 'This page does not exist', 404
        app.error_handlers[404] = page_not_found
    :param code: 對(duì)應(yīng)處理器的整型類型的錯(cuò)誤代碼庄拇。
    """
    def decorator(f):
        self.error_handlers[code] = f  # 將錯(cuò)誤碼和函數(shù)對(duì)象的映射存儲(chǔ)到error_handlers字典
        return f
    return decorator

def before_request(self, f):
    """注冊一個(gè)函數(shù)注服,則每一個(gè)請(qǐng)求處理前調(diào)用。"""
    self.before_request_funcs.append(f)
    return f

def after_request(self, f):
    """注冊一個(gè)函數(shù)措近,在每一個(gè)請(qǐng)求處理后調(diào)用溶弟。"""
    self.after_request_funcs.append(f)
    return f

def context_processor(self, f):
    """注冊一個(gè)模板上下文處理函數(shù)。"""
    self.template_context_processors.append(f)
    return f

#################################
# 下面的幾個(gè)方法用于處理請(qǐng)求和響應(yīng)
#################################

def match_request(self):
    """基于URL映射(map)匹配當(dāng)前請(qǐng)求瞭郑。如果匹配成功辜御,同時(shí)也存儲(chǔ)端點(diǎn)和
    視圖參數(shù),否則存儲(chǔ)異常屈张。
    """
    rv = _request_ctx_stack.top.url_adapter.match()
    request.endpoint, request.view_args = rv
    return rv

def dispatch_request(self):
    """附注請(qǐng)求分發(fā)工作擒权。匹配URL苇本,返回視圖函數(shù)或錯(cuò)誤處理器的返回值。這個(gè)返回值
    不一定得是響應(yīng)對(duì)象菜拓。為了將返回值返回值轉(zhuǎn)換成合適的想要對(duì)象,調(diào)用make_response笛厦。
    """
    try:
        endpoint, values = self.match_request()
        return self.view_functions[endpoint](**values)  # 根據(jù)端點(diǎn)在view_functions字典內(nèi)獲取對(duì)應(yīng)的視圖函數(shù)并調(diào)用纳鼎,傳入視圖參數(shù)
    except HTTPException, e:
        handler = self.error_handlers.get(e.code)
        if handler is None:
            return e
        return handler(e)
    except Exception, e:
        handler = self.error_handlers.get(500)
        if self.debug or handler is None:
            raise
        return handler(e)

def make_response(self, rv):
    """將視圖函數(shù)的返回值轉(zhuǎn)換成一個(gè)真正的響應(yīng)對(duì)象,即response_class實(shí)例裳凸。
    rv允許的類型如下所示:
    ======================= ===============================================
    response_class          這個(gè)對(duì)象將被直接返回
    str                     使用這個(gè)字符串作為主體創(chuàng)建一個(gè)請(qǐng)求對(duì)象
    unicode                 將這個(gè)字符串進(jìn)行utf-8編碼后作為主體創(chuàng)建一個(gè)請(qǐng)求對(duì)象
    tuple                   使用這個(gè)元組的內(nèi)容作為參數(shù)創(chuàng)建一個(gè)請(qǐng)求對(duì)象
    a WSGI function         這個(gè)函數(shù)將作為WSGI程序調(diào)用并緩存為響應(yīng)對(duì)象
    ======================= ===============================================
    :param rv: 視圖函數(shù)返回值
    """
    if isinstance(rv, self.response_class):
        return rv
    if isinstance(rv, basestring):
        return self.response_class(rv)
    if isinstance(rv, tuple):
        return self.response_class(*rv)
    return self.response_class.force_type(rv, request.environ)

def preprocess_request(self):
    """在實(shí)際的請(qǐng)求分發(fā)之前調(diào)用贱鄙,而且將會(huì)調(diào)用每一個(gè)使用before_request
    裝飾的函數(shù)。如果其中某一個(gè)函數(shù)返回一個(gè)值姨谷,這個(gè)值將會(huì)作為視圖返回值
    處理并停止進(jìn)一步的請(qǐng)求處理逗宁。
    """
    for func in self.before_request_funcs:
        rv = func()
        if rv is not None:
            return rv

def process_response(self, response):
    """為了在發(fā)送給WSGI服務(wù)器前修改響應(yīng)對(duì)象,可以重寫這個(gè)方法梦湘。 默認(rèn)
    這會(huì)調(diào)用所有使用after_request裝飾的函數(shù)瞎颗。
    :param response: 一個(gè)response_class對(duì)象。
    :return: 一個(gè)新的響應(yīng)對(duì)象或原對(duì)象捌议,必須是response_class實(shí)例哼拔。
    """
    session = _request_ctx_stack.top.session
    if session is not None:
        self.save_session(session, response)
    for handler in self.after_request_funcs:
        response = handler(response)
    return response

#########################################################################
# WSGI規(guī)定的可調(diào)用對(duì)象,從請(qǐng)求進(jìn)入瓣颅,到生成響應(yīng)并返回的整個(gè)處理流程都發(fā)生在這里
#########################################################################

def wsgi_app(self, environ, start_response):
    """實(shí)際的WSGI程序倦逐。它沒有通過__call__實(shí)現(xiàn),因此可以附加中間件:
    
        app.wsgi_app = MyMiddleware(app.wsgi_app)
    :param environ: 一個(gè)WSGI環(huán)境宫补。
    :param start_response: 一個(gè)接受狀態(tài)碼的可調(diào)用對(duì)象檬姥,一個(gè)包含首部
                           的列表以及一個(gè)可選的用于啟動(dòng)響應(yīng)的異常上下文。
    """
    # 在with語句下執(zhí)行相關(guān)操作粉怕,會(huì)觸發(fā)_RequestContext中的__enter__方法健民,從而推送請(qǐng)求上下文到堆棧中
    with self.request_context(environ):
        rv = self.preprocess_request()  # 預(yù)處理請(qǐng)求,調(diào)用所有使用了before_request鉤子的函數(shù)
        if rv is None:
            rv = self.dispatch_request()  # 請(qǐng)求分發(fā)斋荞,獲得視圖函數(shù)返回值(或是錯(cuò)誤處理器的返回值)
        response = self.make_response(rv)  # 生成響應(yīng)荞雏,把上面的返回值轉(zhuǎn)換成響應(yīng)對(duì)象
        response = self.process_response(response)  # 響應(yīng)處理,調(diào)用所有使用了after_request鉤子的函數(shù)
        return response(environ, start_response)

def request_context(self, environ):
    """從給定的環(huán)境創(chuàng)建一個(gè)請(qǐng)求上下文平酿,并將其綁定到當(dāng)前上下文凤优。這必須搭配with
    語句使用,因?yàn)檎?qǐng)求僅綁定在with塊中的當(dāng)前上下文里蜈彼。
    用法示例:
        
        with app.request_context(environ):
            do_something_with(request)
    :param environ: 一個(gè)WSGI環(huán)境筑辨。
    """
    return _RequestContext(self, environ)

def test_request_context(self, *args, **kwargs):
    """從給定的值創(chuàng)建一個(gè)WSGI環(huán)境(更多信息請(qǐng)參見werkzeug.create_environ,
    這個(gè)函數(shù)接受相同的參數(shù))幸逆。
    """
    return self.request_context(create_environ(*args, **kwargs))

def __call__(self, environ, start_response):
    """wsgi_app的快捷方式棍辕。"""
    return self.wsgi_app(environ, start_response)

本地上下文

請(qǐng)求上下文堆棧(_request_ctx_stack)棧頂(_request_ctx_stack.top)的對(duì)象即請(qǐng)求上下文對(duì)象(_RequestContext)實(shí)例

通過這里的調(diào)用可以獲取當(dāng)前請(qǐng)求上下文中保存的request暮现、session等對(duì)象

請(qǐng)求上下文在wsgi_app方法中通過with語句調(diào)用request_context方法創(chuàng)建并推入堆棧

本地上下文相關(guān)的本地線程、本地堆棧和本地代理的實(shí)現(xiàn)這里不再展開楚昭,你需要先了解堆棧和代碼在Python中的實(shí)現(xiàn)栖袋,

然后再通過閱讀Werkzeug的文檔或源碼了解具體實(shí)現(xiàn)

另外,你也可以閱讀《Flask Web開發(fā)實(shí)戰(zhàn)》(helloflask.com/book)第16章16.4.3小節(jié)抚太,這一小節(jié)首先介紹了本地線程和Werkzeug中實(shí)現(xiàn)的Local塘幅,

然后從堆棧和代理在Python中的基本實(shí)現(xiàn)開始,逐漸過渡到本地堆棧和本地代理的實(shí)現(xiàn)

_request_ctx_stack = LocalStack()
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
request = LocalProxy(lambda: _request_ctx_stack.top.request)
session = LocalProxy(lambda: _request_ctx_stack.top.session)
g = LocalProxy(lambda: _request_ctx_stack.top.g)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尿贫,一起剝皮案震驚了整個(gè)濱河市电媳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌庆亡,老刑警劉巖匾乓,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異又谋,居然都是意外死亡拼缝,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門搂根,熙熙樓的掌柜王于貴愁眉苦臉地迎上來珍促,“玉大人,你說我怎么就攤上這事剩愧≈硇穑” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵仁卷,是天一觀的道長穴翩。 經(jīng)常有香客問我,道長锦积,這世上最難降的妖魔是什么芒帕? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮丰介,結(jié)果婚禮上背蟆,老公的妹妹穿的比我還像新娘。我一直安慰自己哮幢,他們只是感情好带膀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著橙垢,像睡著了一般垛叨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上柜某,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天嗽元,我揣著相機(jī)與錄音敛纲,去河邊找鬼。 笑死剂癌,一個(gè)胖子當(dāng)著我的面吹牛淤翔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播佩谷,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼办铡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了琳要?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤秤茅,失蹤者是張志新(化名)和其女友劉穎稚补,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體框喳,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡课幕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了五垮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乍惊。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖放仗,靈堂內(nèi)的尸體忽然破棺而出润绎,到底是詐尸還是另有隱情,我是刑警寧澤诞挨,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布莉撇,位于F島的核電站,受9級(jí)特大地震影響惶傻,放射性物質(zhì)發(fā)生泄漏棍郎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一银室、第九天 我趴在偏房一處隱蔽的房頂上張望涂佃。 院中可真熱鬧,春花似錦蜈敢、人聲如沸辜荠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侨拦。三九已至,卻和暖如春辐宾,著一層夾襖步出監(jiān)牢的瞬間狱从,已是汗流浹背膨蛮。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留季研,地道東北人敞葛。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像与涡,于是被迫代替她去往敵國和親惹谐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容