日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

flask上下文管理机制

發(fā)布時(shí)間:2023/12/20 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 flask上下文管理机制 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

flask中的上下文分為請(qǐng)求上寫文和應(yīng)用上下文,接下來,從以下三個(gè)大的方面分別探討flask的兩大上下文管理機(jī)制。

  • ?方面一:請(qǐng)求進(jìn)來時(shí)
  • ?方面二:視圖函數(shù)
  • ?方面三:請(qǐng)求結(jié)束前
  • 先來一個(gè)最簡單的flask版的Hello World

    from flask import Flaskapp = Flask(__name__)@app.route('/') def index():return "Hello World"if __name__ == '__main__':app.run()Flask版Hello World flask版的Hello World

    啟動(dòng)一個(gè)flask項(xiàng)目時(shí),會(huì)先執(zhí)行app.run()方法,這是整個(gè)項(xiàng)目的入口,執(zhí)行run方法時(shí),接著執(zhí)行werkzeug模塊中的run_simple

    此時(shí)的self指的是我們實(shí)例化的Flask對(duì)象,即app,當(dāng)請(qǐng)求到來時(shí),會(huì)觸發(fā)調(diào)用Flask的__call__方法。

    一、請(qǐng)求進(jìn)來時(shí)

    觸發(fā)執(zhí)行__call__方法,__call__方法的邏輯很簡單,直接執(zhí)行wsgi_app方法,將包含所有請(qǐng)求相關(guān)數(shù)據(jù)和一個(gè)響應(yīng)函數(shù)傳進(jìn)去。

    ?此時(shí)的self指的是Flask的實(shí)例化對(duì)象app,接下來執(zhí)行app的wsgi_app方法。

    此時(shí)的self仍然指的是Flask的實(shí)例化對(duì)象app,首先執(zhí)行app這個(gè)對(duì)象的request_context的方法,將environ傳進(jìn)去,最后返回一個(gè)RequestContext類的對(duì)象,對(duì)象中封裝了request和session等實(shí)例變量。ctx=RequestContext()

    RequestContext類在實(shí)例化過程中執(zhí)行__init__方法,賦了兩個(gè)非常有用的屬性,一個(gè)是request,一個(gè)是session

    這兩個(gè)屬性中request是一個(gè)Request()對(duì)象,這個(gè)對(duì)象就是我們?cè)趂lask中使用的request對(duì)象,為我們提供了很多便捷的屬性和方法,比如:request.method、request.form、request.args等等,另一個(gè)屬性是session,初始為None。

    緊接著執(zhí)行ctx.push()方法,此時(shí)的self為ctx

    這個(gè)方法中,在執(zhí)行請(qǐng)求上下文對(duì)象ctx之前先執(zhí)行的app的app_context()方法,返回值為AppContext()的實(shí)例化對(duì)象,即app_ctx = AppContext(),先執(zhí)了AppContext()的push方法,然后才執(zhí)行_request_ctx_stack對(duì)象中的top和_request_ctx_stack.push(self),最后對(duì)ctx中的session進(jìn)行處理。

    所以,flask中的應(yīng)用上下文發(fā)生在請(qǐng)求上下文之前

    但是我們先說請(qǐng)求上下文,在處理完應(yīng)用上下文的push方法后,緊接著執(zhí)行了_request_ctx_stack對(duì)象的兩個(gè)方法。

    而這個(gè)_request_ctx_stack是LocalStack這個(gè)類的對(duì)象。_request_ctx_stack = LocalStack()

    ?

    LocalStack有沒有很眼熟,沒錯(cuò),flask內(nèi)部使用的機(jī)制就是類似于我們上文中自定義的LocalStack的機(jī)制,實(shí)例化過程中使用了面向?qū)ο笾械慕M合概念,self._local = Local(),然后在自身又實(shí)現(xiàn)了push、pop、top方法,這三個(gè)方法中都是通過反射從Local類的實(shí)例化對(duì)象中對(duì)一個(gè)stack屬性進(jìn)行append、pop、[-1]的操作,所以,Local對(duì)象中的stack屬性對(duì)應(yīng)的值一定是一個(gè)類似于列表的東西。通過對(duì)列表的操作,實(shí)現(xiàn)一個(gè)類似于棧的存取。

    ?接下來我們開始執(zhí)行LocalStack()對(duì)象的push方法

    在此方法中執(zhí)行了self._local.stack = rv =[ ],self._local為Local類的實(shí)例化對(duì)象,Local類在實(shí)例化的過程中,會(huì)對(duì)每個(gè)對(duì)象生成__storage__和__indent_func__兩個(gè)實(shí)例變量,__storage__對(duì)應(yīng)的是一個(gè)空字典{},__ident_func__對(duì)應(yīng)的一個(gè)獲取當(dāng)前線程ID的一個(gè)可執(zhí)行函數(shù)get_ident。

    ?

    我們翻遍整個(gè)Local類的源碼,發(fā)現(xiàn)內(nèi)部并沒有實(shí)現(xiàn)一個(gè)叫stack的方法或者屬性,但是上面我們提到了LocalStack對(duì)象會(huì)對(duì)Local對(duì)象中的一個(gè)叫stack的東西進(jìn)行一系列操作。找不到不會(huì)報(bào)錯(cuò)嗎?

    ?這就是flask的巧妙之處,通過類的一些魔法方法巧妙的實(shí)現(xiàn)了相應(yīng)的處理。如果對(duì)象中沒有某個(gè)屬性,取值時(shí)會(huì)執(zhí)行類中的__getattr__方法,賦值時(shí)會(huì)執(zhí)行類中的__setattr__方法。

    ?

    ?

    處理完_request_ctx_stack后,就該處理session了。

      在flask中,處理session時(shí),非常的巧妙,完美的遵循了開閉原則,會(huì)先執(zhí)行session_interface對(duì)象的open_session方法,在這個(gè)方法中,會(huì)先從用戶請(qǐng)求的cookie中獲取sessionid,獲取該用戶之前設(shè)置的session值,然后將值賦值到ctx.session中。

      處理完session后,ctx.push方法就執(zhí)行完了,返回到最開始的app.wsgi_app方法中,執(zhí)行完push方法后,接著執(zhí)行full_dispatch_request方法,從這個(gè)名字中我們也能猜到,這個(gè)方法只要是負(fù)責(zé)請(qǐng)求的分發(fā)。

    def full_dispatch_request(self):self.try_trigger_before_first_request_functions()try:request_started.send(self)rv = self.preprocess_request()if rv is None:rv = self.dispatch_request()except Exception as e:rv = self.handle_user_exception(e)return self.finalize_request(rv) full_dispath_request方法 full_dispatch_request

    在full_dispatch_request方法中先執(zhí)行preprocess_request方法,這個(gè)方法,會(huì)先執(zhí)行所有被before_request裝飾器裝飾的函數(shù),然后就通過路由的分發(fā)執(zhí)行視圖函數(shù)了(dispatch_request)

    ?二、執(zhí)行視圖函數(shù)時(shí)

    在執(zhí)行視圖函數(shù)之前,先執(zhí)行了before_request,在執(zhí)行我們的視圖函數(shù)。

    視圖函數(shù)主要處理業(yè)務(wù)邏輯。在視圖函數(shù)中可以調(diào)用request對(duì)象,進(jìn)行取值,也可以調(diào)用session對(duì)象對(duì)session的存取。

    在整個(gè)request的請(qǐng)求生命周期中,獲取請(qǐng)求的數(shù)據(jù)直接調(diào)用request即可,對(duì)session進(jìn)行操作直接調(diào)用session即可。request和session都是LocalProxy對(duì)象,借助偏函數(shù)的概念將對(duì)應(yīng)的值傳入_lookup_req_object函數(shù)。先從_request_ctx_stack(LocalStack)對(duì)象中獲取ctx(請(qǐng)求上下文對(duì)象),再通過反射分別獲取request和session屬性。整個(gè)過程中LocalStack扮演了一個(gè)全局倉庫的角色,請(qǐng)求進(jìn)來將數(shù)據(jù)存取,需要時(shí)即去即用。所以,flask實(shí)現(xiàn)了在整個(gè)請(qǐng)求的生命周期中哪兒需要就直接調(diào)用的特色。

    request = LocalProxy(partial(_lookup_req_object, 'request')) session = LocalProxy(partial(_lookup_req_object, 'session'))

    三、請(qǐng)求結(jié)束前

    視圖函數(shù)執(zhí)行完后,dispatch_request執(zhí)行結(jié)束,執(zhí)行full_dispatch_request方法的返回值finalize_request方法。這個(gè)方法中,同樣的,在返回響應(yīng)之前,先執(zhí)行所有被after_request裝飾器裝飾的函數(shù)。

    ---->finalize_request ------> process_response

    ?

    def process_response(self, response):ctx = _request_ctx_stack.topbp = ctx.request.blueprintfuncs = ctx._after_request_functionsif bp is not None and bp in self.after_request_funcs:funcs = chain(funcs, reversed(self.after_request_funcs[bp]))if None in self.after_request_funcs:funcs = chain(funcs, reversed(self.after_request_funcs[None]))for handler in funcs:response = handler(response)if not self.session_interface.is_null_session(ctx.session):self.session_interface.save_session(self, ctx.session, response)return responseprocess_response

    執(zhí)行process_response過程中,執(zhí)行完after_request后,然后,執(zhí)行session的save_session方法。將內(nèi)存中保存在ctx.session的值取到后,json.dumps()序列化后,寫入響應(yīng)的cookie中(set_cookie),最后返回響應(yīng)。

    ?

    def save_session(self, app, session, response):domain = self.get_cookie_domain(app)path = self.get_cookie_path(app)# If the session is modified to be empty, remove the cookie.# If the session is empty, return without setting the cookie.if not session:if session.modified:response.delete_cookie(app.session_cookie_name,domain=domain,path=path)return# Add a "Vary: Cookie" header if the session was accessed at all.if session.accessed:response.vary.add('Cookie')if not self.should_set_cookie(app, session):returnhttponly = self.get_cookie_httponly(app)secure = self.get_cookie_secure(app)samesite = self.get_cookie_samesite(app)expires = self.get_expiration_time(app, session)val = self.get_signing_serializer(app).dumps(dict(session))# set_cookie將session寫入響應(yīng)的cookie中 response.set_cookie(app.session_cookie_name,val,expires=expires,httponly=httponly,domain=domain,path=path,secure=secure,samesite=samesite)save_session

    返回響應(yīng)后,自動(dòng)的調(diào)用ctx.auto_pop(error),將Local中存儲(chǔ)的ctx對(duì)象pop掉,整個(gè)請(qǐng)求結(jié)束。

    ?四、應(yīng)用上下文

    與請(qǐng)求上下文類似,當(dāng)請(qǐng)求進(jìn)來時(shí),先實(shí)例化一個(gè)AppContext對(duì)象app_ctx,在實(shí)例化的過程中,提供了兩個(gè)有用的屬性,一個(gè)是app,一個(gè)是g。self.app就是傳入的全局的app對(duì)象,self.g是一個(gè)全局的存儲(chǔ)值的對(duì)象。接著將這個(gè)app_ctx存放到LocalStack()。

    class AppContext(object):def __init__(self, app):self.app = appself.url_adapter = app.create_url_adapter(None)self.g = app.app_ctx_globals_class()

    視圖函數(shù)中,我們就可以調(diào)用app對(duì)象和g對(duì)象,如果我們使用藍(lán)圖構(gòu)建我們的項(xiàng)目時(shí),在每一個(gè)直接引用app就會(huì)造成循環(huán)引用的異常,這時(shí),應(yīng)用上下文就會(huì)顯得非常有用,我們可以直接調(diào)用current_app就可以在整個(gè)生命周期中使用我們的app對(duì)象了。比如使用我們的配置項(xiàng):current_app.config

    current_app = LocalProxy(_find_app)

    最后,當(dāng)視圖函數(shù)執(zhí)行結(jié)束后,從storage中pop掉app_ctx對(duì)象。

    總結(jié):

    流程:請(qǐng)求到來:- 請(qǐng)求到來之后wsgi會(huì)觸發(fā)__call__方法,由__call__方法再次調(diào)用wsgi_app方法,將request和session相關(guān)封裝到ctx = RequestContext對(duì)象中。將app和g封裝到app_ctx = AppContext對(duì)象中。再通過LocalStack對(duì)象將ctx、app_ctx封裝到Local對(duì)象中。獲取數(shù)據(jù):通過LocalProxy對(duì)象+偏函數(shù),調(diào)用LocalStack去Local中獲取響應(yīng)ctx、app_ctx中封裝的值。請(qǐng)求結(jié)束:調(diào)用LocalStack的pop方法,將ctx和app_ctx移除。

    ?

    轉(zhuǎn)載于:https://www.cnblogs.com/fengchong/p/10256552.html

    總結(jié)

    以上是生活随笔為你收集整理的flask上下文管理机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。