Python学习:Python Web Server Gateway Interface v1.0 中文版

PEP 333 - Python Web Server Gateway Interface v1.0 中文版

============

译者的话

Python基础学完后,免不了要深入到Python的主流Web框架(Python科学计算那部分暂时用不到可以先不管),在学习Flask这些框架的过程中发现它们的底层都是WSGI协议,故决定先啃下WSGI,鉴于目前网上几乎没有(完整的)WSGI中文版,于是干脆自己翻译,这样也有助于加深自己的理解,同时也能够帮助到一些初学者。

内容

序言

注意:关于本规范的后续版本,请参照 PEP 3333,PEP 3333是支持Python 3.x 的新版本,同时还包含了一些社区勘误,补充,更正的相关说明信息。

摘要

这份规范规定了一种在web服务器与web应用程序/框架之间推荐的标准接口,以确保web应用程序在不同的web服务器之间具有可移植性。

基本原理及目标

Python目前拥有大量的web框架,比如 Zope,Quixote,Webware, SkunkWeb,PSO 和Twisted Web – 这里我仅列举出这么几个。这么多的选择让新手无所适从,因为整体上,选择什么样的框架有时会反过来限制对web服务器的选择。

相比之下,虽然java也拥有众多web的框架,但是java的 servlet API 使得用任何框架编写出来的应用程序都可以在所有支持 servlet API 的web服务器上运行。

服务器中这种针对Python的API的使用和普及(不管服务器是用python写的(如: Medusa),还是内嵌python(如: mod_python),亦或是通过一种网关协议来调用Python(如:CGI, FastCGI等)),把人们从web框架的选择和web服务器的选择中剥离开来,使他们能够任意选择适合自己的组合,而web服务器和web框架的开发者们也能够把精力集中到各自的领域。

鉴于此,这份PEP建议在web服务器和web应用程序/web框架之间建立一种简单通用的接口规范,即Python Web服务器网关接口(简称WSGI)。

但是光有这么一份规范,对于改变web服务器和web应用程序/框架的现状还是不够的,只有当那些web服务器和web框架的作者/维护者们真正地实现了WSGI,这份WSGI规范才能起到它该起的作用。

不过,由于目前还没有任何框架或服务器实现了WSGI,而那些新转向支持WSGI的框架作者们也不会从我们这得到任何直接的奖励或者好处,所以,我们的这份WSGI必须要拟定地足够容易实现,这样才能降低框架作者们在实现接口这件事上的初始投资成本。

由此可见,服务器和框架两边接口实现的简单性,对于提高WSGI的实用性来说,绝对是非常重要的,同时,这一点也是任何设计决策的首要依据。

然而需要注意的是,框架作者实现框架时的简单性和web应用程序开发者使用框架时的易用性是两码事。WSGI为框架作者们提出了一套只包含必需、最基本元素的接口,因为像响应对象以及 cookie 处理等这些花哨的高级功能只会妨碍现有的框架对这些问题的处理。再说一次,WSGI的目标是使现有的web服务器和web框架之间更加方便地互联互通,而不是想重新创建一套新的web框架。

同时也要注意到,我们的这个目标也限制了WSGI不会用到任何当前版本的Python里没有的东西。因此,这一份规范中不会推荐或要求任何新的Python标准模块,WSGI中规定的所有东西都不需要2.2.2以上版本的Python支持。(当然,在未来版本的Python标准库中,倘若Python自带的标准库中的Web服务器能够包含对我们这份接口的支持,那将会是一个很不错的主意。)

除了要让现有的以及将要出现的框架和服务器容易实现之外,也应该让创建诸如请求预处理器(request preprocessors)、响应处理器(response postprocessors)及其他基于WSGI的中间件组件这一类事情变得简单易操作。这里说的中间件组件,它们是这样一种东西:对服务器来说它们是应用程序,而对中间件包含的应用程序来说,它们又可以被看作是服务器。

如果中间件既简单又鲁棒,并且WSGI可以广泛地应用在服务器和框架中,那么就有可能出现全新的Python web框架:一个由若干个WSGI中间件组件组成的松耦合的框架。事实上,现有框架的作者们甚至可能会选择去重构他们框架中已有的服务,使它们变得更像是一些配合WSGI使用的库而不是一个完整的框架。这样一来,web应用程序开发者们这就可以为他们想实现的特定功能选择最佳组合的组件,而不用再局限于某一个特定框架并忍受该框架的所有优缺点。

当然,就现在来说,这一天毫无疑问还要等很久。同时,对WSGI来说,让每一个框架都能在任何服务器上运行起来,又是一个十足的短期目标。

最后,需要指出的是,此版本的WSGI对于一个应用程序具体该以何种方式部署在web服务器或者服务器网关上并没有做具体说明。就现在来看,这个是需要由服务器或网关来负责定义怎么实现的。等到以后,等有了足够多的服务器/网关通过实现了WSGI并积累了多样化的部署需求方面的领域经验,那么到时候也许会产生另一份PEP来描述WSGI服务器和应用框架的部署标准。

规范概述

WSGI接口可以分为两端:服务器/网关端和应用程序/Web框架端。服务器端调用一个由应用程序端提供的可调用者(Callable),至于它是如何被调用的,这要取决于服务器/网关这一端。我们假定有一些服务器/网关会要求应用程序的部署人员编写一个简短的脚本来启动一个服务器/网关的实例,并提供给服务器/网关一个应用程序对象,而还有的一些服务器/网关则不需要这样,它们会需要一个配置文件又或者是其他机制来指定应该从哪里导入或者获得应用程序对象。

除了单纯的服务器/网关和应用程序/框架,还可以创建一种叫做中间件的组件,中间件它对这份规范当中的两端(服务器端和应用程序端)都做了实现,我们可以这样解释中间件,对于包含它们的服务器,中间件是应用程序,而对于包含在中间件当中的应用程序来说,它又扮演着服务器的角色。不仅如此,中间件还可以用来提供可扩展的API,以及内容转换,导航和其他有用的功能。

在这份规范说明书中,我们将使用的术语"callable(可调用者)",它的意思是"一个函数,方法,类,或者拥有 call 方法的一个对象实例",这取决于服务器,网关,或者应用程序根据需要而选择的合适的实现技术。相反,服务器,网关,或者请求一个可调用者(callable)的应用程序必须不依赖可调用者(callable)的具体提供方式。记住,可调用者(callable)只是被调用,不会自省(introspect)。[译者注:introspect,自省,Python的强项之一,指的是代码可以在内存中象处理对象一样查找其它的模块和函数。]

应用程序/框架端

一个应用程序对象简单地说就是一个接受了2个参数的可调用对象(callable object),这里的对象并不能理解为它真的需要一个对象实例:一个函数、方法、类、或者带有 __call__ 方法的对象实例都可以用来当做应用程序对象。应用程序对象必须可以被多次调用,实质上所有的服务器/网关(除了CGI)都会产生这样的重复请求。

(注意:虽然我们把它叫做“应用程序”对象,但这并不意味着程序员需要把WSGI当做API来调用!我们假定应用程序开发者将会仍然使用更高层的框架服务来开发它们的应用程序,WSGI只是一个提供给框架和服务器开发者们使用的工具,它并没有打算直接向应用程序开发者提供支持。)

这里我们来看两个应用程序对象的示例:其中,一个是函数,另一个是类:

def simple_app(environ, start_response):
    """这可能是最简单的应用程序对象了。"""
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return ['Hello world!\n']


class AppClass:
    """生成相同的输出,但是使用的是一个类。
    (注意:这里‘AppClass’就是一个“应用程序”,故调用它会返回一个‘AppClass’的实例,这个实例就是规范里面说的由一个“可调用的应用程序(application callable)”返回的可迭代者(iterable)。

    如果我们希望使用‘AppClass’的实例,而不是应用程序对象,那么我们就必须实现这个‘__call__’方法,这个方法将用来执行应用程序,然后我们需要创建一个实例来提供给服务器/网关使用。
    """

    def __init__(self, environ, start_response):
        self.environ = environ
        self.start = start_response

    def __iter__(self):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Hello world!\n"
服务器/网关 端

每一次,当HTTP客户端冲着应用程序发来一个请求,服务器/网关都会调用应用程序可调用者(callable)。为了阐述方便,这里有一个CGI网关,简单的说它就是一个以应用程序对象为参数的函数实现,注意,本例中对错误只做了有限的处理,因为默认情况下没有被捕获到的异常都会被输出到sys.stderr并被服务器记录下来。

import os, sys

def run_with_cgi(application):

    environ = dict(os.environ.items())
    environ['wsgi.input']        = sys.stdin
    environ['wsgi.errors']       = sys.stderr
    environ['wsgi.version']      = (1, 0)
    environ['wsgi.multithread']  = False
    environ['wsgi.multiprocess'] = True
    environ['wsgi.run_once']     = True

    if environ.get('HTTPS', 'off') in ('on', '1'):
        environ['wsgi.url_scheme'] = 'https'
    else:
        environ['wsgi.url_scheme'] = 'http'

    headers_set = []
    headers_sent = []

    def write(data):
        if not headers_set:
             raise AssertionError("write() before start_response()")

        elif not headers_sent:
             # 在第一次输出之前发送已存储的报头。
             status, response_headers = headers_sent[:] = headers_set
             sys.stdout.write('Status: %s\r\n' % status)
             for header in response_headers:
                 sys.stdout.write(
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值