<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
今天在 git.oschina 的首頁上看到他們推出演示平臺,其中,Python 的演示平臺支援 WSGI 介面的應用。雖然,這個演示平臺連它自己提供的範例都跑不起來,但是,它還是成功的勾起了我對 WSGI 的好奇心。一番瞭解,對該機制的認識,總結如下。如有不妥,還望斧正。
寫過網頁應用的各位親,應該對 CGI 有了解,我們知道,CGI 的全程是“Common Gateway Interface”,即 “通用 Gateway Interface“。沒錯,這裡的 WSGI,就是隻針對 Python的網頁應用介面“Python Web Server Gateway Interface”。通過這樣的類比,想必大家對他的地位就有所瞭解了。
它只是一個介面定義:它不負責伺服器的實現,也不負責網頁應用的實現,它只是一個兩邊介面方式的約定。所以,它並不是另一個 WEB 應用框架。通常意義上的 WEB 應用框架,也只相當於 WSGI 網頁應用端的一種實現。
這樣做的好處是?PEP 0333 中的解釋是,為了實現一個類似於 Java Servelet 的 API,使得遵循該介面的應用擁有更廣泛的適用性。是的,有了該介面,你就不用去考慮,伺服器對 Python 的支援到底是如何實現——無論是“ 直接用 Python 實現的伺服器”,還是“伺服器嵌入 Python”,或者是 “ 通過閘道器介面(CGI, Fastcgi...)”——應用程式都有很好的適用性。就像是今天故事的開始,我們遇到了雲平臺,它提供了對 WSGI 介面的支援,那麼,只要應用是基於 WSGI 的,那麼應用就可以直接跑起來。
此外,WSGI 的設計,也提供了另外一種可能性,那就是中介軟體(middleware)。或者說,我們可以寫一些對 server 和 application 都相容的模組,我們可以把他們部署在 Server 端,也可以部署在 Application 端,完成比如快取、字元編碼轉換、根據 url 做應用 routing 等功能。這種設計模式,是 WSGI 降低了 server 和 application 耦合度之後的產物,同時,它從另一個角度大大提升了設計的靈活性。
上一小節,簡要對 WSGI 做了介紹。這裡從 application、server、middleware 三個角度對 WSGI 稍微進行深入,使我們對它有一個更具體的印象。
WSGI 要求,應用端必須提供一個可被呼叫的實體(PEP 0333 使用的是 Object,檔案還特別解釋這有別於Object instance),該實體可以是:一個函數(function)、一個方法(method)、一個類(class)、或者是有__call__方法的物件(Object instance)。
這裡有兩個網頁應用端的實現範例,一個是 function object,一個 class object:
def simple_app(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return ['Hello world!n']
上面的 function 只是直接對請求直接做了 “200 ok” 迴應,並沒有處理傳進來的引數 environ——裡面是由 WSGI Server 端提供的各種 HTTP 請求引數。需要特別注意的是,這個函數在最後,返回的一個 list(用“[]”包含在內)以保證結果的 iterable。下面的 class 類似。
在下面例子中,AppClass 作為應用實體。當呼叫發生時,其實是對 class 進行了例化( python 固有特性,可以參考後面 server 端的實現程式碼進一步理解),正如我們看到,這次呼叫(call)的返回值也是可迭代的——雖然只迭代一次(yield)。
class AppClass: 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!n" """ In fact, the interator ‘ends‘ here because of no more yield field"""
與上面兩種情形不同,使用 object instance 作為應用實體時,需要為類定義新增 __call__ 方法,同時,參考上面使用 function 作為實體時情形,__call__ 方法的返回值需為 iterable(比如 return [ something ])。
最後,不管我們的 app 是 function 還是 class, application 都需要處理兩個引數,而且是兩個位置相關的引數(不是命名引數),分別是:一個存放了 CGI 環境變數的 dictionary object,和一個可呼叫實體(需要給它三個位置相關的引數,兩個必須,一個可選)。
其中,可呼叫實體(前例中的 start_response)必須呼叫一次,兩個必須的引數分別為“ HTTP Response的狀態(str 型別)“ 和 “HTTP Response Header(list of tuples)“;
一個可選的引數exc_info,必須是 Python sys.exc_info() tuple,只有在出錯需要顯示錯誤資訊時使用。完整呼叫:start_response(status, response_headers,exc_info).
下面是從 PEP 0333 拿來的一個簡單的 WSGI 容器,適用於 Python 作為某 WEB Server 上 CGI 時的應用情形。
import os, sys def run_with_cgi(application): environ = dict(os.environ.items()) environ['wsgi.input'] = sys.stdin 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): 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 sys.stdout.write('Status: %srn' % status) for header in response_headers: sys.stdout.write('%s: %srn' % header) sys.stdout.write('rn') sys.stdout.write(data) sys.stdout.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[0], exc_info[1], 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()
上面的容器,大概實現了:
因為 WSGI 的寬鬆耦合的特性,我們可以輕鬆的在 Application 和 Server 之前插入任何的中間外掛,在不需要改動 Server 和 Application 的前提下,實現一些特殊功能。但是,這種放在 Server 和 Application “中間”的模組,並不是這裡要講的 middleware ;或者,這隻能算是一種特殊的 middleware,因為它僅僅是實現了 PEP 0333 中 middleware 定義的 Application 側的功能。這種僅實施在一側的 middleware,需要在釋出時,特別的宣告。
PEP 0333 中約定,中介軟體是一些即可以在 Server 端實施,又可以在 Application 端實施的模組。所以,在設計的時候,對兩邊的特性都要做適當考慮。幸好,WSGI 介面設計的足夠簡單。
class Router(): def __init__(self): self.path_info = {} def route(self, environ, start_response): application = self.path_info[environ['PATH_INFO']] return application(environ, start_response) def __call__(self, path): def wrapper(application): self.path_info[path] = application return wrapper """ The above is the middleware""" router = Router() @router('/world') def world(environ, start_response): status = '200 OK' output = 'World!'start_response(status, response_headers) return [output] @router('/hello') def hello(environ, start_response): status = '200 OK' output = 'Hello' response_headers = [('Content-type', 'text/plain'), ('Content-Length', str(len(output)))] start_response(status, response_headers) return [output]
簡單解釋一下:
- 作為 Application 時,我們用 Router 範例化一個物件。然後對 “ PATH-APP “ 進行註冊,根據不同的 PATH,我們要進一步選擇哪個 App。接著,就是把 router.route() 餵給 Server ,作為 Application 側的可呼叫實體。有請求到來時,根據已經註冊的 “PATH-APP” 對選擇應用並執行。
- Server 端類似,我們要先範例化並完成註冊。然後,比如,拿我們上一小節實現的 WSGI 容器為例,我們需要修改 result = router.route(environ, start_response),同樣完成了router的功能。
下面是另外一個,實現了 postprocessor 的一個例子,在 Application 返回的 HTTP Header 裡面再加一個 Header。
def myapp(environ, start_response): response_headers = [('content-type', 'text/plain')] start_response('200 OK', response_headers) return ['Check the headers!'] class Middleware: def __init__(self, app): self.wrapped_app = app def __call__(self, environ, start_response): def custom_start_response(status, headers, exc_info=None): headers.append(('X-A-SIMPLE-TOKEN', "1234567890")) return start_response(status, headers, exc_info) return self.wrapped_app(environ, custom_start_response) app = Middleware(myapp)
這裡通過改寫傳遞給 Application 的實體,實現了 postprocess 的目的。
- WSGI 的一些詳細資料,包括應用列表什麼的:https://wsgi.readthedocs.io/en/latest/
- 支援 WSGI 的多執行緒 WEB 伺服器,基於SimpleHttpServer:
http://www.owlfish.com/software/wsgiutils/
-Paste為構建以 WSGI 為基礎的 WEB 應用程式或框架提供一個良好的基礎
- 官方的 WSGI 實現參考:https://pypi.org/project/wsgiref/
- 啄木鳥社群的 WSGI 中文 wiki:https://wiki.woodpecker.org.cn/moin/WSGI
- 和 Paste 一樣有名的基本架構:https://pypi.org/project/Pylons/1.0/
- 目前 Python 比較流行的三大 WEB 框架:TurboGears,Django,web2py。+1,程式碼在 K 級別的服務小框架:webpy。
- 另外三個據說高效能的 App 開發框架:Falcon、Tornado、Bootle.py.
- 還有個價格不錯的 vps,恩:https://www.hostwinds.com/
以上就是通過Python中的CGI介面講解什麼是WSGI的詳細內容,更多關於Python中CGI介面講解WSGI的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45