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的简单应用。

参考


Werkzeug 中文文档
Werkzeug 英文文档
WSGI Servers
WSGI协议的原理和实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • [TOC]一直想做源码阅读这件事,总感觉难度太高时间太少,可望不可见。最近正好时间充裕,决定试试做一下,并记录一下...
    何柯君阅读 7,033评论 3 98
  • Refer to: www.threemeal.com/blog/12/ 中间件 中间件是一个钩子框架,它们可以介...
    兰山小亭阅读 16,375评论 9 166
  • 原文:https://legacy.python.org/dev/peps/pep-3333 PEP:3333标题...
    老周_o_o阅读 2,356评论 0 5
  • 人们总是希望速成,越想速成就越浮躁,浮躁就会蒙蔽我们的心眼,做事也就做不到本质上,会给人一种你不可信的感觉。这是一...
    阿怪lm阅读 151评论 0 1