Werkzeug 與 WSGI 介紹


Werkzeug 是一個WSGI工具包,也可以作為一個Web框架的底層庫。

WSGI


在介紹Werkzeug之前,先介紹一下 WSGI(Python Web Server Gateway Interface),它為Python語言定義的Web服務(wù)器和Web應(yīng)用程序或框架之間的一種簡單而通用的接口。這是一個規(guī)范,描述了web server如何與web application交互、web application如何處理請求,該規(guī)范的具體描述在PEP3333,強(qiáng)烈推薦先閱讀 PEP3333 再回頭來閱讀本文。

WSGI 分為兩個部分:

  • Server/Gateway: 即是HTTP Server, 負(fù)責(zé)從客戶端(Nnginx、apache、IIS)接收請求,將 request 轉(zhuǎn)發(fā)給 application, 并將 application(可能是個Flask應(yīng)用) 返回的response 返回給客戶端
  • Application/Framework: 一個python web 應(yīng)用或 web 框架接收由 server 轉(zhuǎn)發(fā)的request,處理請求,并將處理結(jié)果返回給 server

可以通過下面兩張圖片來梳理一下它們之間的調(diào)用關(guān)系:


wsgi server 與 wsgi 應(yīng)用之間的調(diào)用關(guān)系
web sever 與 wsgi server 與 web browser之間的調(diào)用流程

先從一份示例代碼理解:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return ['Hello World!']

一個最基本的 WSGI 應(yīng)用就是如上所示,定義了一個 application 函數(shù)(callable object),callable object(可調(diào)用對象) 包括: 一個函數(shù)、方法、類或一個實(shí)現(xiàn)了__call__的實(shí)例都可以用作應(yīng)用程序?qū)ο?。這個函數(shù)接受兩個參數(shù),分別是environ和start_response。

  • environ是一個字典包含了CGI中的環(huán)境變量
  • start_response也是一個callable,接受兩個必須的參數(shù),status(HTTP狀態(tài))和response_headers(響應(yīng)消息的頭)

通過回調(diào)函數(shù)(start_response)將響應(yīng)狀態(tài)和響應(yīng)頭返回給 server,同時返回響應(yīng)正文(response body),響應(yīng)正文是可迭代的、并包含了多個字符串。

Werkzeug


werkzeug 提供了 python web WSGI 開發(fā)相關(guān)的功能:

  • 路由處理:如何根據(jù)請求 URL 找到對應(yīng)的視圖函數(shù)
  • request 和 response 封裝: 提供更好的方式處理request和生成response對象
  • 自帶的 WSGI server: 測試環(huán)境運(yùn)行WSGI應(yīng)用

下面使用 Werkzeug 來實(shí)現(xiàn)一個簡單的WSGI應(yīng)用:

from werkzeug.wrappers import Request, Response

def application(environ, start_response):
    request = Request(environ)
    text = 'Hello %s!' % request.args.get('name', 'World')
    response = Response(text, mimetype='text/plain')
    return response(environ, start_response)

如上代碼所示,請求數(shù)據(jù)需要環(huán)境對象,Werkzeug允許你以一個輕松的方式訪問數(shù)據(jù)。響應(yīng)對象是一個 WSGI 應(yīng)用,提供了更好的方法來創(chuàng)建響應(yīng)。

具體創(chuàng)建一個 WSGI 應(yīng)用請查看文檔,后面會陸續(xù)提到Flask框架中使用到Werkzeug的數(shù)據(jù)結(jié)構(gòu)。這里貼一些官方文檔的例子,使用werkzeug創(chuàng)建一個web 應(yīng)用:

import os
import redis
import urlparse
from werkzeug.wrappers import Request, Response
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException, NotFound
from werkzeug.wsgi import SharedDataMiddleware
from werkzeug.utils import redirect
from jinja2 import Environment, FileSystemLoader

class Shortly(object):
    """ 
    Shortly 是一個實(shí)際的 WSGI 應(yīng)用,通過 __call__ 方法直接調(diào) 用 wsgi_app,
    同時通過一個可選設(shè)置創(chuàng)建一個中間件,將static文件夾暴露給用戶:
    """
    def __init__(self, config):
        self.redis = redis.Redis(config['redis_host'], config['redis_port'])

    def dispatch_request(self, request):
        return Response('Hello World!')

    def wsgi_app(self, environ, start_response):
        request = Request(environ)
        response = self.dispatch_request(request)
        return response(environ, start_response)

    def __call__(self, environ, start_response):
        return self. wsgi_app(environ, start_response)


def create_app(redis_host='localhost', redis_port=6379, with_static=True):
    app = Shortly({
        'redis_host':       redis_host,
        'redis_port':       redis_port
    })
    if with_static:
        app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
            '/static':  os.path.join(os.path.dirname(__file__), 'static')
        })
    return app
    
if __name__ == '__main__':
    from werkzeug.serving import run_simple
    app = create_app()
    run_simple('127.0.0.1', 5000, app, use_debugger=True, use_reloader=True)

思路很簡單,我們的 Shortly 是一個實(shí)際的 WSGI 應(yīng)用。 __call__ 方法直接調(diào)用 wsgi_app 。這樣做我們可以裝飾 wsgi_app 調(diào)用中間件,就像我們在 create_app 函數(shù)中做的一樣。 wsgi_app 實(shí)際上創(chuàng)建了一個 Request 對象,之后通過 dispatch_request 調(diào)用 Request 對象然后給 WSGI 應(yīng)用返回一個 Response 對象。正如你看到的:無論是創(chuàng)建 Shortly 類,還是創(chuàng)建 Werkzeug Request 對象來執(zhí)行 WSGI 接口。最終結(jié)果只是從 dispatch_request 方法返回另一個 WSGI 應(yīng)用。這部分解釋來源于官方文檔的中文版。

總結(jié)


本文主要解釋了WSGI規(guī)范和Werkzeug(WSGI 工具集),以及如何實(shí)現(xiàn)一個符合WSGI規(guī)范的WSGI應(yīng)用,最后使用Werkzeug 工具集中的相關(guān)模塊,快速實(shí)現(xiàn)了一個基于WSGI的簡單應(yīng)用。

參考


Werkzeug 中文文檔
Werkzeug 英文文檔
WSGI Servers
WSGI協(xié)議的原理和實(shí)現(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,680評論 19 139
  • [TOC]一直想做源碼閱讀這件事,總感覺難度太高時間太少,可望不可見。最近正好時間充裕,決定試試做一下,并記錄一下...
    何柯君閱讀 7,400評論 3 98
  • Refer to: www.threemeal.com/blog/12/ 中間件 中間件是一個鉤子框架,它們可以介...
    蘭山小亭閱讀 16,799評論 9 164
  • 原文:https://legacy.python.org/dev/peps/pep-3333 PEP:3333標(biāo)題...
    老周_o_o閱讀 2,495評論 0 5
  • 人們總是希望速成,越想速成就越浮躁,浮躁就會蒙蔽我們的心眼,做事也就做不到本質(zhì)上,會給人一種你不可信的感覺。這是一...
    阿怪lm閱讀 232評論 0 1

友情鏈接更多精彩內(nèi)容