python web 应用:wsgi基础-亚博电竞手机版

本文由码农网 – 肖豪原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

在django,flask,bottle和其他一切python web 框架底层的是web server gateway interface,简称wsgi。wsgi对python来说就像 servlets对java一样——一种用于web服务器并允许不同web服务器和应用框架基于通用api交互的通用规范。然而,对于大多数事情,python版本实现相当简单。

wsgi被定义在pep 3333协议里面,如果在读完本文之后你想学到更多东西的话,作者建议读者先阅读一下简介。

本文将从一个应用开发者的角度来向你介绍wsgi说明,并且向你展示怎样直接通过wsgi来开发应用程序(如果你迫不及待的话)。

你的第一个wsgi应用

下面是最基本的python web应用:

def app(environ, start_fn):     start_fn('200 ok', [('content-type', 'text/plain')])     return ["hello world!\n"]

就是这样!整个文件。先命名为app.py然后在任何wsgi可编译服务器上面运行,然后你就可以得到一个hello world并伴随一个200的响应状态码。你可以使用gunicorn来完成,通过pip(pip install gunicorn)来安装并执行gunicorn app:app。这条命令告诉gunicorn从应用模块里的应用变量去获取可调用的wsgi。

刚才,十分兴奋吧。仅仅三行代码就可以运行一个应用?那一定是某种意义上的记录(不包括php,因为mod_php在起作用)。我敢打赌你现在一定想更加深入了解下去了。

所以一个wsgi应用最重要的部分是什么呢?

  • 一个wsgi应用是python可调用的,就像一个函数,一个类,或者一个有__call__方法的类实例
  • 可调用的应用程序必须接受两个参数:environ,一个包含必要数据的python字典,start_fn,它自己是可调用的。
  • 应用程序必须能调用start_fn和两个参数:状态码(字符串),和一个头部以两个元组表述的列表。
  • 应用程序返回一个在返回体里包含bytes的方便的可迭代对象,流式的部分——例如,一个个只包含“hello,world!”字符串的列表。(如果app是一个类的话,可以在__iter__方法里完成)

例如下面两个例子和第一个是等价的:

class app(object):      def __init__(self, environ, start_fn):         self.environ = environ         self.start_fn = start_fn      def __iter__(self):         self.start_fn('200 ok', [('content-type', 'text/plain')])         yield "hello world!\n"
class application(object):     def __call__(self, environ, start_fn):         start_fn('200 ok', [('content-type', 'text/plain')])         yield "hello world!\n"  app = application()

你可能已经开始思考能用这些东西来做什么了,但是最可能相关的一项是用来写中间件。

让它活跃起来

中间件是一种方便扩展wsgi应用功能性的方法。因为你只需提供一个可调用的对象,你可以任意把它包裹在其他函数里。

例如,假设我们想检测一下environ里面的内容。我们可以轻易地创建一个中间件来完成,如下所示:

import pprint  def handler(environ, start_fn):     start_fn('200 ok', [('content-type', 'text/plain')])     return ["hello world!\n"]  def log_environ(handler):     def _inner(environ, start_fn):         pprint.pprint(environ)         return handler(environ, start_fn)     return _inner  app = log_environ(handler)

这里,log_environ是一个返回函数的函数,它在environ参数延迟原始回调之前装饰该参数。

这样写中间件的优点是中间件和处理器不需要知道或者关心对方。你可以轻易地绑定log_environ到一个flask应用上面,例如,因为flask应用是wsgi应用。

其他一些有用的中间件设计:

import pprint  def handle_error(handler):     def _inner(environ, start_fn):         try:             return handler(environ, start_fn)         except exception as e:             print e  # log error             start_fn('500 server error', [('content-type', 'text/plain')])             return ['500 server error']     return _inner  def wrap_query_params(handler):     def _inner(environ, start_fn):         qs = environ.get('query_string')         environ['query_params'] = urlparse.parse_qs(qs)         return handler(environ, start_fn)     return _inner

如果你不想让你的文件有一个很大的金字塔底的话,你可以使用reduce一次应用到多个中间件上。

# applied from bottom to top on the way in, then top to bottom on the way out middlewares = [wrap_query_params,                log_environ,                handle_error]  app = reduce(lambda h, m: m(h), middlewares, handler)

利用start_fn参数的优点你也可以写装饰响应体的中间件。下面是一个内容类型头部是text/plain反转输出结果的中间件。

def reverser(handler):      # a reverse function     rev = lambda it: it[::-1]      def _inner(environ, start_fn):         do_reverse = []  # must be a reference type such as a list          # override start_fn to check the content type and set a flag         def start_reverser(status, headers):             for name, value in headers:                 if (name.lower() == 'content-type'                         and value.lower() == 'text/plain'):                     do_reverse.append(true)                     break              # remember to call `start_fn`             start_fn(status, headers)          response = handler(environ, start_reverser)          try:             if do_reverse:                 return list(rev(map(rev, response)))              return response         finally:             if hasattr(response, 'close'):                 response.close()     return _inner

由于start_fn和响应体的分离有一点混乱,但是还是完美运行的。

同时请注意,为了严格遵循wsgi规范,如果存在的话,我们必须在响应体里检查close方法并且调用它。wsgi应用程序有可能返回一个write函数而不是一个调用处理器的可迭代对象,如果你想要你的中间件支持更早的应用的话,你可能需要处理这个问题。

一旦你开始一点点把玩原生wsgi,你就开始理解为什么python有一堆web框架。wsgi让从底层造一些东西变得十分简单。例如,你也许正在考虑下面的路由问题:

routes = {     '/': home_handler,     '/about': about_handler, }  class application(object):     def __init__(self, routes):         self.routes = routes      def not_found(self, environ, start_fn):         start_fn('404 not found', [('content-type', 'text/plain')])         return ['404 not found']      def __call__(self, environ, start_fn):         handler = self.routes.get(environ.get('path_info')) or self.not_found         return handler(environ, start_fn)

如果你喜欢下面资源集合的灵活性的话,直接使用wsgi造轮子还是十分方便的。

  • 模板库:投入任何你喜欢的模板(例如,jinja2,pystashe)然后从你的处理器返回渲染后的模板!
  • 用一个库帮助你的路由,如routes或者werkzeug’s routing。实际上,如果你想轻松使用wsgi的话,看一看werkzeug吧。
  • 使用任何flask或者相似的数据库迁移库。

当然,对于非专业的应用,你也可能想使用一下框架,那样的话,一些特殊的诸如此类的例子也可以得到合理解决。

关于服务器呢?

有许许多多服务wsgi应用的方法。我们已经讨论过gunicorn了,一个相当好的选择。uwsgi是另外一个不错的选择。但是请确保在服务这些静态东西之前设置好nginx一类的东西,并且你应该有一个固定的开始节点。

译文链接:http://www.codeceo.com/article/python-web-wsgi.html 英文原文:python web applications: the basics of wsgi 翻译作者:码农网 – 肖豪 [ 转载必须在正文中标注并保留原文链接、译文链接和译者等信息。]

展开全文
内容来源于互联网和用户投稿,文章中一旦含有亚博电竞手机版的联系方式务必识别真假,本站仅做信息展示不承担任何相关责任,如有侵权或涉及法律问题请联系亚博电竞手机版删除

最新文章

网站地图