flask請求和應用上下文

關於WSGI

WSGI(全稱Web Server Gateway Interface),是爲 Python 語言定義的Web服務器Web應用程序之間的一種簡單而通用的接口,它封裝了接受HTTP請求解析HTTP請求發送HTTP響應等等的這些底層的代碼和操做,使開發者能夠高效的編寫Web應用。php

一個簡單的使用WSGI的App例子:html

def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [b'<h1>Hello, I Am WSGI!</h1>'] 
  • environ: 一個包含所有HTTP請求信息的字典,由WSGI Server解包HTTP請求生成。
  • start_response: 一個WSGI Server提供的函數,調用能夠發送響應的狀態碼和HTTP報文頭, 函數在返回前必須調用一次start_response()
  • application()應當返回一個能夠迭代的對象(HTTP正文)。
  • application()函數由WSGI Server直接調用和提供參數。
  • Python內置了一個WSGIREFWSGI Server,不過性能不是很好,通常只用在開發環境。能夠選擇其餘的如Gunicorn
 
WSGI Server 和 App交互圖

Flask的上下文對象

Flask有兩種Context(上下文),分別是python

  • RequestContext 請求上下文
  • Request 請求的對象,封裝了Http請求(environ)的內容
  • Session 根據請求中的cookie,從新載入該訪問者相關的會話信息。
  • AppContext 程序上下文
  • g 處理請求時用做臨時存儲的對象。每次請求都會重設這個變量
  • current_app 當前激活程序的程序實例

生命週期:瀏覽器

  • current_app的生命週期最長,只要當前程序實例還在運行,都不會失效。
  • Requestg的生命週期爲一次請求期間,當請求處理完成後,生命週期也就完結了
  • Session就是傳統意義上的session了。只要它還未失效(用戶未關閉瀏覽器、沒有超過設定的失效時間),那麼不一樣的請求會共用一樣的session。

Flask處理流程

 
Flask處理請求流程

第一步:建立上下文

Flask根據WSGI Server封裝的請求等的信息(environ)新建RequestContext對象AppContext對象ruby

# 聲明對象 # LocalStack LocalProxy 都由Werkzeug提供 # 咱們不深究他的細節,那又是另一個故事了,咱們只需知道他的做用就好了 # LocalStack 是棧結構,能夠將對象推入、彈出 # 也能夠快速拿到棧頂對象。固然,全部的修改都只在本線程可見。 _request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack() # 若是調用一個LocalStack實例, 能返回一個 LocalProxy 對象 # 這個對象始終指向 這個LocalStack實例的棧頂元素。 # 若是棧頂元素不存在,訪問這個 LocalProxy 的時候會拋出 RuntimeError異常 # LocalProxy對象你只需暫時理解爲棧裏面的元素便可了 current_app = LocalProxy(_find_app) request = LocalProxy(partial(_lookup_req_object, 'request')) session = LocalProxy(partial(_lookup_req_object, 'session')) g = LocalProxy(partial(_lookup_app_object, 'g')) 
# RequestContext class RequestContext(object): def __init__(self, app, environ, request=None): self.app = app if request is None: request = app.request_class(environ) self.request = request self.url_adapter = app.create_url_adapter(self.request) self.flashes = None self.session = None 
#AppContext class AppContext(object): def __init__(self, app): self.app = app self.url_adapter = app.create_url_adapter(None) self.g = app.app_ctx_globals_class() self._refcnt = 0 

這裏須要注意的是,RequestContext在初始化的時候,當前Flask的實例做爲參數被傳進來。雖然每次的請求處理都會建立一個RequestContext對象,可是每一次傳入的app參數倒是同一個。經過這個機制,可使得:bash

由同一個Flask實例所建立的RequestContext,其成員變量app都是同一個Flask實例對象 。實現了多個RequestContext對應同一個current_app 的目的。服務器

第二步:入棧


RequestContext對象push進_request_ctx_stack裏面。在此次請求期間,訪問request對象session對象將指向這個棧的棧頂元素cookie

class RequestContext(object): def push(self): .... _app_ctx_stack.push(self) appcontext_pushed.send(self.app) 

AppContext對象push進_app_ctx_stack裏面。在此次請求期間,訪問g對象將指向這個棧的棧頂元素session

class AppContext(object): def push(self): .... _request_ctx_stack.push(self) 

 

第三步:請求分發

response = self.full_dispatch_request()
Flask將調用full_dispatch_request函數進行請求的分發,之因此不用給參數,是由於咱們能夠經過request對象得到此次請求的信息。full_dispatch_request將根據請求的url找到對應的藍本里面的視圖函數,並生成一個response對象。注意的是,在請求以外的時間,訪問request對象是無效的,由於request對象依賴請求期間的_request_ctx_stack棧。app

 

第四步:上下文對象出棧

此次HTTP的響應已經生成了,就不須要兩個上下文對象了。分別將兩個上下文對象出棧,爲下一次的HTTP請求作出準備。

第五步:響應WSGI

調用Response對象,向WSGI Server返回其結果做爲HTTP正文。Response對象是一個 可調用對象,當調用發生時,將首先執行WSGI服務器傳入的start_response()函數 發送狀態碼和HTTP報文頭。

最後附上Flask處理請求的wsgi_app函數

# environ: WSGI Server封裝的HTTP請求信息 # start_response: WSGI Server提供的函數,調用能夠發送狀態碼和HTTP報文頭 def wsgi_app(self, environ, start_response): # 根據environ建立上下文 ctx = self.request_context(environ) # 把當前的request context,app context綁定到當前的context ctx.push() error = None try: try: #根據請求的URL,分發請求,通過視圖函數處理後返回響應對象 response = self.full_dispatch_request() except Exception as e: error = e response = self.make_response(self.handle_exception(e)) return response(environ, start_response) finally: if self.should_ignore_error(error): error = None # 最後出棧 ctx.auto_pop(error)
相關文章
相關標籤/搜索