標籤:web python 架構
前提
一個web架構需要包含的組件或者功能有:
request and response
cookies and session
template engine
wsgi app and wsgi server
對於所有的Web應用,本質上其實就是一個socket服務端,使用者的瀏覽器其實就是一個socket用戶端。
wsgi
首先要瞭解WSGI的相關知識,如下:
web server:指的是軟體程式,它從用戶端接受請求,然後返回一個Response,需要注意的是它不建立Response,web app才是建立Response的主體。
web app:根據url來建立響應並將響應傳回給web server。
WSGI:是一個規範,描述了web server如何與web app互動、web app如何處理請求,它規定每個app必須是一個可調用的對象(方法或者類),接受兩個參數environ和start_response。
實現web server
environ和start_response都是伺服器提供的,所以服務端這邊需要提供如下:
準備environ參數;
定義start_response函數;
調用程式端的可調用對象;
代碼具體如下(取自PEP 333):
import os, sysenc, esc = sys.getfilesystemencoding(), ‘surrogateescape‘def unicode_to_wsgi(u): # Convert an environment variable to a WSGI "bytes-as-unicode" string return u.encode(enc, esc).decode(‘iso-8859-1‘)def wsgi_to_bytes(s): return s.encode(‘iso-8859-1‘)def run_with_cgi(application): # environ參數,裡面是HTTP請求的環境變數 environ = {k: unicode_to_wsgi(v) for k,v in os.environ.items()} environ[‘wsgi.input‘] = sys.stdin.buffer environ[‘wsgi.errors‘] = sys.stderr environ[‘wsgi.version‘] = (1, 0) environ[‘wsgi.multithread‘] = False environ[‘wsgi.multiprocess‘] = True environ[‘wsgi.run_once‘] = True if environ.get(‘HTTPS‘, ‘off‘) in (‘on‘, ‘1‘): environ[‘wsgi.url_scheme‘] = ‘https‘ else: environ[‘wsgi.url_scheme‘] = ‘http‘ headers_set = [] headers_sent = [] # 將應答的資料輸出到終端 def write(data): out = sys.stdout.buffer if not headers_set: raise AssertionError("write() before start_response()") elif not headers_sent: # Before the first output, send the stored headers status, response_headers = headers_sent[:] = headers_set out.write(wsgi_to_bytes(‘Status: %s\r\n‘ % status)) for header in response_headers: out.write(wsgi_to_bytes(‘%s: %s\r\n‘ % header)) out.write(wsgi_to_bytes(‘\r\n‘)) out.write(data) out.flush() # 根據程式傳過來的狀態和頭部參數設定響應的狀態和頭部 def start_response(status, response_headers, exc_info=None): if exc_info: try: if headers_sent: # Re-raise original exception if headers sent raise exc_info[1].with_traceback(exc_info[2]) finally: exc_info = None # avoid dangling circular ref elif headers_set: raise AssertionError("Headers already set!") headers_set[:] = [status, response_headers] return write # 調用用戶端的對象,並且輸出返回的結果 result = application(environ, start_response) try: for data in result: if data: # don‘t send headers until body appears write(data) if not headers_sent: write(‘‘) # send headers now if body was empty finally: if hasattr(result, ‘close‘): result.close()
web app
因為服務端和用戶端需要共同遵守WSGI協議內容,所以用戶端這邊需要使用到服務端提供的environ和start_response,web app需要返回的類型需要是可迭代的。這裡分為三種類型的實現,分別是函數,類,執行個體;
函數
HELLO_WORLD = b"Hello world!\n"# 1. 可調用對象是一個函數def application(environ, start_response): # HTTP response code and message status = ‘200 OK‘ # 應答的頭部是一個列表,每對索引值都必須是一個 tuple。 response_headers = [(‘Content-Type‘, ‘text/plain‘), (‘Content-Length‘, str(len(HELLO_WORLD)))] # 調用伺服器程式提供的 start_response,填入兩個參數 start_response(status, response_headers) # 返回必須是 iterable return [HELLO_WORLD]
類
# 2. 可調用對象是一個類class AppClass: """這裡的可調用對象就是 AppClass 這個類,調用它就能產生可以迭代的結果。 使用方法類似於: for result in AppClass(env, start_response): do_somthing(result) """ def __init__(self, environ, start_response): self.environ = environ self.start = start_response def __iter__(self): status = ‘200 OK‘ response_headers = [(‘Content-type‘, ‘text/plain‘)] self.start(status, response_headers) yield HELLO_WORLD
執行個體
# 3. 可調用對象是一個執行個體class AppClass: """這裡的可調用對象就是 AppClass 的執行個體,使用方法類似於: app = AppClass() for result in app(environ, start_response): do_somthing(result) """ def __init__(self): pass def __call__(self, environ, start_response): status = ‘200 OK‘ response_headers = [(‘Content-type‘, ‘text/plain‘)] self.start(status, response_headers) yield HELLO_WORLD
Web架構原理