一、WSGI
在介绍Werkzeug之前,先介绍一下 WSGI(Python Web Server Gateway Interface),它为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。这是一个规范,描述了web server如何与web application交互、web application如何处理请求,该规范的具体描述在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 Server 与 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应用: ```python 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 应用:```pythonimport osimport redisimport urllib.parsefrom werkzeug.wrappers import Request, Responsefrom werkzeug.routing import Map, Rulefrom werkzeug.exceptions import HTTPException, NotFoundfrom werkzeug.wsgi import SharedDataMiddlewarefrom werkzeug.utils import redirectfrom jinja2 import Environment, FileSystemLoaderclass 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 appif __name__ == '__main__':from werkzeug.serving import run_simpleapp = 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 应用。这部分解释来源于官方文档的中文版。

