1. 程式人生 > >Werkzeug 與 WSGI 介紹

Werkzeug 與 WSGI 介紹

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

WSGI

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

WSGI 分為兩個部分:

  • Server/Gateway: 即是HTTP Server, 負責從客戶端(Nnginx、apache、IIS)接收請求,將 request 轉發給 application, 並將 application(可能是個Flask應用) 返回的response 返回給客戶端
  • Application/Framework: 一個python web 應用或 web 框架接收由 server 轉發的request,處理請求,並將處理結果返回給 server

可以通過下面兩張圖片來梳理一下它們之間的呼叫關係: wsgi server 與 wsgi 應用之間的呼叫關係

web sever 與 wsgi server 與 web browser之間的呼叫流程

先從一份示例程式碼理解:

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

一個最基本的 WSGI 應用就是如上所示,定義了一個 application 函式(callable object),callable object(可呼叫物件) 包括:實現了__call__

方法的函式、方法、類或例項都可以用作應用程式物件。這個函式接受兩個引數,分別是environ和start_response。

  • environ是一個字典包含了CGI中的環境變數
  • start_response也是一個callable,接受兩個必須的引數,status(HTTP狀態)和response_headers(響應訊息的頭)

通過回撥函式(start_response)將響應狀態和響應頭返回給 server,同時返回響應正文(response body),響應正文是可迭代的、幷包含了多個字串。

Werkzeug

werkzeug 提供了 python web WSGI 開發相關的功能:

  • 路由處理:如何根據請求 URL 找到對應的檢視函式
  • request 和 response 封裝: 提供更好的方式處理request和生成response物件
  • 自帶的 WSGI server: 測試環境執行WSGI應用

下面使用 Werkzeug 來實現一個簡單的WSGI應用:

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)

如上程式碼所示,請求資料需要環境物件,Werkzeug允許你以一個輕鬆的方式訪問資料。響應物件是一個 WSGI 應用,提供了更好的方法來建立響應。

具體建立一個 WSGI 應用請檢視文件,後面會陸續提到Flask框架中使用到Werkzeug的資料結構。這裡貼一些官方文件的例子,使用werkzeug建立一個web 應用:

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 是一個實際的 WSGI 應用,通過 __call__ 方法直接調 用 wsgi_app,
    同時通過一個可選設定建立一箇中間件,將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 是一個實際的 WSGI 應用。 __call__ 方法直接呼叫 wsgi_app 。這樣做我們可以裝飾 wsgi_app 呼叫中介軟體,就像我們在 create_app 函式中做的一樣。 wsgi_app 實際上建立了一個 Request 物件,之後通過 dispatch_request 呼叫 Request 物件然後給 WSGI 應用返回一個 Response 物件。正如你看到的:無論是建立 Shortly 類,還是建立 Werkzeug Request 物件來執行 WSGI 介面。最終結果只是從 dispatch_request 方法返回另一個 WSGI 應用。這部分解釋來源於官方文件的中文版。

總結

本文主要解釋了WSGI規範和Werkzeug(WSGI 工具集),以及如何實現一個符合WSGI規範的WSGI應用,最後使用Werkzeug 工具集中的相關模組,快速實現了一個基於WSGI的簡單應用。

參考