`
androider
  • 浏览: 751876 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

用 Python WSGI 混和并匹配 Web 组件

阅读更多

转:http://www.ibm.com/developerworks/cn/web/wa-wsgi/index.html?S_TACT=105AGX52&S_CMP=09-w-cto

学习有关构建具有最大灵活性的 Web 应用程序的 Python 标准

2006 年 8 月 22 日

学习使用 Python 在 Web 服务器中创建并重用组件。Python 社区创建了 Web 服务器网关接口(Web Server Gateway Interface,WSGI),这是创建跨服务器和框架工作的 Python Web 组件的标准。它提供了一种利用许多不同的 Web 工具开发 Web 应用程序的方法。本文介绍了 WSGI 并展示如何开发出能够集成到设计出色的 Web 应用程序中的组件。

 

Web 成功的主要原因是它的灵活性。您会发现在设计、开发和部署 Web 站点和应用程序方面,每个开发人员几乎都有自己的特色。尽管有大量的选择,但是一个 Web 开发人员经常选择惟一的 Web 设计工具、页面风格、内容语言、Web 服务器、中间件和 DBMS 技术的组合,使用不同的实现语言和辅助工具包。为了给所有这些元素一起工作提供最大的灵活性,应该尽可能地通过组件提供 Web 的功能。这些组件应该执行有限数量的任务,并相互很好地配合工作。这么说很容易,但是在实践中完成这一要求是非常困难的,因为有许多实现 Web 技术的不同方法。

因此开发人员期望着 Web 组件协同工作标准的成熟。这些重要标准的一部分已经开发完成,而且最成功的 Web 开发平台已经成为它们的支柱。突出的例子包括 Java servlet API 和 Ruby on Rails 框架。一些长期流行的 Web 编程语言直到最近才提供了相同的组件化水平,而且借鉴了先前 Web 框架组件标准的经验。一个例子是 Zend Framework for PHP(参见 参考资料)。另一个例子是 Python 的 Web 服务器网关接口(WSGI)。

许多人抱怨流行的 Python 编程语言有太多的 Web 框架,从众所周知的 entrant(比如 Zope)到 under-the-radar 框架(比如 SkunkWeb)。有些人认为只要有一些基础标准化了,这种差异可能是件好事。Python 和 Web 专家 Phillip J. Eby 从事这样的标准化工作。他撰写的 Python Enhancement Proposal(PEP)333 定义了 WSGI。

WSGI 的目标是支持 Python 框架之间更大的协同工作能力。WSGI 的成功会产生一个插件组件生态系统,在这个系统中可以用您喜欢的框架获得最大的灵活性。在本文中,我将介绍 WSGI,主要关注它作为可重用 Web 组件架构的使用方法。在所有讨论和示例代码中,假设您正在使用 Python 2.4 或者更新的版本。

WSGI 的基本架构

WSGI 是在相当严格的约束条件下开发的,但是最重要的是需要对先于它的 Web 框架的向下兼容性。很遗憾,这个约束意味着 WSGI 不能像 Python 开发人员习惯的那么简洁和透明。通常,必须直接处理 WSGI 的开发人员只有那些需要构建框架和可重用组件的开发人员。大多数普通的 Web 开发人员会挑选容易使用的框架,而不接触 WSGI 的细节。

如果想开发可重用 Web 组件,就必须理解 WSGI。需要理解的第一件事是以 WSGI 的角度来看 Web 应用程序是如何构造的。图 1 说明了这个结构。


图 1. HTTP 请求 — 响应如何通过 WSGI 堆栈 
 

Web 服务器(也称为网关)是与请求客户机(通常是用户的浏览器)进行基本通信的非常低级的代码。应用层处理较高级别的细节,解释来自用户的请求并准备响应的内容。WSGI 本身的应用程序接口通常只是应用程序框架的更高层的最基本层,为通用的 Web 模式(比如 Ajax 技术或者内容模板系统)提供友好的设施。在服务器或者网关层之上是 WSGI 中间件。这个重要的层包含在服务器和应用程序实现中可以共享的组件。通用 Web 特性(比如用户会话、错误处理和身份验证)可以作为 WSGI 中间件来实现。

中间的代码

WSGI 中间件对于可重用组件是最自然的层。WSGI 中间件看上去对于应用程序是较低层,而对于服务器是较高层。为了添加一些特殊的特性,它监视请求、响应和 WSGI 环境的状态。遗憾的是,WSGI 规范提供了非常差的中间件例子,而且可以找到的大多数其他例子也很简单,只能提供如何快速编写中间件的感性认识。我将使用以下概述给出对 WSGI 中间件承担的过程的感性认识。这里忽略了大多数 WSGI 中间件作者都不必担心的问题。在 Python 中,我使用单词函数,意思是任何可调用的对象。

  • 设置阶段。每当 Web 服务器启动时,设置阶段就发生了。它接受一个中间件实例,其中包括应用程序函数。
  • 处理客户机请求。每当 Web 服务器收到请求时,就会处理客户机请求。
    1. 服务器用这个环境和 server.start_response 参数调用中间件函数。
    2. 中间件处理这个环境并调用可调用的应用程序,传递这个环境和包装的函数 middleware.start_response
    3. 应用程序执行;首先它准备响应报头,然后调用 middleware.start_response
    4. 中间件处理响应报头并调用 server.start_response
    5. 服务器将控制传递回中间件,然后回到开始产生响应体块(比如字符串)的应用程序。
    6. 对于每一个响应,体块中间件做出任何修改并向服务器传递一些相应的字符串。
    7. 一旦处理完应用程序的所有块,中间件就向服务器返回控制,完成当前的请求。

 


 

处理 XHTML 的大胆步骤

许多组件技术相当复杂,所以作为指导的例子最好是简单的东西。这不是使用 WSGI 的案例,事实上,我将要介绍一个非常实际的例子。许多开发者更喜欢使用 XHTML Web 页面,因为 XML 技术比 “标记汤” 的 HTML 更容易管理,而且 Web 站点倾向于更容易由机器读取。问题是并非所有的 Web 浏览器都能正确地支持 XHTML。清单 1(safexhtml.py)是一个 WSGI 中间件模块,它检查到来的请求,看浏览器是否支持 XHTML;如果不支持,就将任何 XHTML 响应翻译成普通的 HTML。可以使用这样的模块让所有主要应用程序代码产生 XHTML,而中间件负责任何需要翻译成 HTML 的地方。仔细查看 清单 1 并试着把它与前面描述的 WSGI 中间件过程相结合。我提供了足够的注释,您可以识别出代码的不同阶段。


清单 1(safexhtml.py). WSGI 中间件为不能处理 XHTML 的浏览器把 XHTML 翻译成 HTML


import cStringIO
from xml import sax
from Ft.Xml import CreateInputSource
from Ft.Xml.Sax import SaxPrinter
from Ft.Xml.Lib.HtmlPrinter import HtmlPrinter

XHTML_IMT = "application/xhtml+xml"
HTML_CONTENT_TYPE = 'text/html; charset=UTF-8'

class safexhtml(object):
    """
    Middleware that checks for XHTML capability in the client and translates
    XHTML to HTML if the client can't handle it
    """
    def __init__(self, app):
        #Set-up phase
        self.wrapped_app = app
        return

    def __call__(self, environ, start_response):
        #Handling a client request phase.
        #Called for each client request routed through this middleware

        #Does the client specifically say it supports XHTML?
        #Note saying it accepts */* or application/* will not be enough
        xhtml_ok = XHTML_IMT in environ.get('HTTP_ACCEPT', '')

        #Specialized start_response function for this middleware
        def start_response_wrapper(status, response_headers, exc_info=None):
            #Assume response is not XHTML; do not activate transformation
            environ['safexhtml.active'] = False
            #Check for response content type to see whether it is XHTML
            #That needs to be transformed
            for name, value in response_headers:
                #content-type value is a media type, defined as
                #media-type = type "/" subtype *( ";" parameter )
                if ( name.lower() == 'content-type'
                     and value.split(';')[0] == XHTML_IMT ):
                    #Strip content-length if present (needs to be
                    #recalculated by server)
                    #Also strip content-type, which will be replaced below
                    response_headers = [ (name, value)
                        for name, value in response_headers
                            if ( name.lower()
                                 not in ['content-length', 'content-type'])
                    ]
                    #Put in the updated content type
                    response_headers.append(('content-type', HTML_CONTENT_TYPE))
                    #Response is XHTML, so activate transformation
                    environ['safexhtml.active'] = True
                    break

            #We ignore the return value from start_response
            start_response(status, response_headers, exc_info)
            #Replace any write() callable with a dummy that gives an error
            #The idea is to refuse support for apps that use write()
            def dummy_write(data):
                raise RuntimeError('safexhtml does not support the deprecated 
                                       write() callable in WSGI clients')
            return dummy_write

        if xhtml_ok:
            #The client can handle XHTML, so nothing for this middleware to do
            #Notice that the original start_response function is passed
            #On, not this middleware's start_response_wrapper
            for data in self.wrapped_app(environ, start_response):
                yield data
        else:
            response_blocks = []  #Gather output strings for concatenation
            for data in self.wrapped_app(environ, start_response_wrapper):
                if environ['safexhtml.active']:
                    response_blocks.append(data)
                    yield '' #Obey buffering rules for WSGI
                else:
                    yield data

            if environ['safexhtml.active']:
                #Need to convert response from XHTML to HTML 
                xhtmlstr = ''.join(response_blocks) #First concatenate response

                #Now use 4Suite to transform XHTML to HTML
                htmlstr = cStringIO.StringIO()  #Will hold the HTML result
                parser = sax.make_parser(['Ft.Xml.Sax'])
                handler = SaxPrinter(HtmlPrinter(htmlstr, 'UTF-8'))
                parser.setContentHandler(handler)
                #Don't load the XHTML DTDs from the Internet
                parser.setFeature(sax.handler.feature_external_pes, False)
                parser.parse(CreateInputSource(xhtmlstr))
                yield htmlstr.getvalue()
                return



 

safexhtml 类是完整的中间件实现。每个实例都是一个可调用对象,因为这个类定义了专门的 __call__ 方法。向服务器传递类的实例,传递包装成初始化器 __init__ 的应用程序。如果把 safexhtml 链到另一个中间件,包装的应用程序可能也是另一个中间件实例。当中间件作为向服务器请求的结果被调用时,这个类首先检查客户机发送的 Accept 报头,看它是否包括正式的 XHTML 媒体类型。如果是这样的话(xhtml_ok 标志),那么发送 XHTML 就是安全的,中间件就不在请求中做任何有意义的事情。

当客户机不能处理 XHTML 的时候,这个类定义专门的嵌套函数 start_response_wrapper,这个函数的作用是检查来自应用程序的响应报头,看响应是否是 XHTML。如果是,这个响应需要翻译成普通的 HTML,在这个环境中以 safexhtml.active 标记这个事实。对于这个标志,使用这个环境的一个原因是因为它处理将标志返回到其余的中间件代码的范围问题。记住,在应用程序选择的时间异步调用 start_response_wrapper,它可以技巧地管理必要的中间件状态。

使用这个环境的另一个原因是向下沿着 WSGI 堆栈通信时,内容已经修改。如果需要翻译响应体,不仅要运行 start_response_wrapper 设置 safexhtml.active,而且它还把响应媒体类型改成 text/html 并删除任何 Content-Length 报头,因为翻译会改变响应体的长度,它必须向下游重新计算,这可能由服务器执行。

一旦应用程序开始发送响应体,如果需要翻译,它就把数据收集到 response_blocks 列表中。应用程序可能以块的形式发送响应,但是为了保持代码简单,它仅针对完整的 XHTML 输入运行翻译机制。可是,WSGI 规则规定应用程序每次产生块的时候,中间件必须向服务器传送一些东西。传递空字符串也是可以的,这就是它应该做的。一旦应用程序完成,它就固定响应体并通过翻译代码处理响应,然后用最后一个字符串产生完整的输出结果。

清单 2(wsgireftest.py)是测试中间件的服务器代码。它使用包含非常简单的 WSGI 服务器的 wsgiref。这个模块将包括在 Python 2.5 标准库中。


清单 2(wsgireftest.py). 测试清单 1 的服务器代码

import sys
from wsgiref.simple_server import make_server
from safexhtml import safexhtml

XHTML = open('test.xhtml').read()
XHTML_IMT = "application/xhtml+xml"
HTML_IMT = "text/html"
PORT = 8000


def app(environ, start_response):
    print "using IMT", app.current_imt
    start_response('200 OK', [('Content-Type', app.current_imt)])
    #Swap the IMT used for response (alternate between XHTML_IMT and HTML_IMT)
    app.current_imt, app.other_imt = app.other_imt, app.current_imt
    return [XHTML]

app.current_imt=XHTML_IMT
app.other_imt=HTML_IMT

httpd = make_server('', PORT, safexhtml(app))
print 'Starting up HTTP server on port %i...'%PORT

# Respond to requests until process is killed
httpd.serve_forever()


 

清单 2 读取 清单 3(test.xhtml)给出的简单 XHTML 文件,并向它提供替代的媒体类型。对于第一次请求使用标准的 XHTML 媒体类型,对于第二次使用 HTML 媒体类型,第三次又使用 XHTML,如此循环。如果响应没有标成 XHTML,就保持响应不变,这个中间件功能留给读者作为练习。


清单 3(test.xhtml). 由清单 2 中的示例服务器使用的简单 XHTML 文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
  <head>
    <title>Virtual Library</title>
  </head>
  <body>
    <p>Moved to <a href="http://vlib.org/">vlib.org</a>.</p>
  </body>
</html>


 

如果运行 清单 2 并在理解 XHTML 的浏览器(比如 Firefox)以及不理解 XHTML 的浏览器(比如 Microsoft Internet Explorer)中观看,您应该能够看到这个中间件的效果。为了在每个浏览器中看到中间件操作对响应媒体类型的影响,要在一行中请求两次。使用 View Source 查看产生的响应体,使用 Page Info 特性查看报告的响应媒体类型。也可以使用命令行 HTTP 工具 cURL:用 curl -H 'Accept: application/xhtml+xml,text/html' http://localhost:8000/ 模拟理解 XHTML 的浏览器,用 curl -H 'Accept: text/html' http://localhost:8000/ 模拟不理解 XHTML 的浏览器,从而测试这个例子。如果想要看响应报头,在每一次调用 cURL 之后使用 -D <filename> 并检查给定的文件名。

 




回页首

 

结束语

现在您学习了 Python 的 WSGI 以及如何使用它实现可以插入任何 WSGI 服务器和应用程序链的中间件服务。很容易将本文的中间件例子链到进行缓存或者调试的中间件。所有这些都成为组件,因此可以快速地向项目中添加经过反复测试的特性,而不必考虑选择的 WSGI 实现是什么。

WSGI 是一个相当新的规范,但是兼容的服务器、中间件和工具很快出现,使得 Python Web 框架的前景非常光明。下一次您在 Python 中开发重大的 Web 项目时,一定要通过使用现有的 WSGI 组件来采用 WSGI,还可以考虑为自己或者为您的 Web 开发伙伴创建 WSGI 组件。

 

参考资料

学习


获得产品和技术


讨论

 

关于作者

Uche Ogbuji 的照片

Uche Ogbuji 是 Fourthought Inc. 的顾问和合作创始人。Fourthought 公司是一家软件供应商和咨询公司,致力于企业知识管理的 XML 解决方案。Fourthought 公司开发了 4Suite,这是一个用于 XML、RDF 和知识管理应用程序的开放源码平台。Ogbuji 先生还是 Versa RDF 查询语言的首席开发人员。Ogbuji 先生是一名计算机工程师和作家。他出生在尼日利亚,现在美国科罗拉多州的波德市工作和生活。可以通过他的博客 Copia 找到有关他的更多信息,还可以通过 uche@ogbuji.net 与他联系。

  • 大小: 10.6 KB
分享到:
评论

相关推荐

    cpp-modwsgi是一个Apache模块实现了PythonWSGI接口服务

    这个模块,通常称为`mod_wsgi`,使得开发者可以使用Python语言来编写Web应用,并在Apache环境下运行。在本文中,我们将深入探讨`mod_wsgi`的工作原理、其重要性以及如何使用它。 首先,让我们理解WSGI。Web服务器...

    windows下使用flask+wsgi+Apache部署python web.rar

    windows下使用flask+wsgi+Apache部署python web, 博客地址 https://blog.csdn.net/Albert201605/article/details/115429256

    Linux+Django+Python+Wsgi配置过程

    ### Linux+Django+Python+Wsgi配置过程 #### 一、环境准备与系统基本信息 根据提供的文件信息,本文档将详细介绍如何在Linux环境下配置Apache+Mod_Wsgi+Django环境的过程。具体步骤包括软件安装、项目部署等环节。...

    Python WSGI的深入理解

    An Introduction to the Python Web Server Gateway Interface (WSGI) 可以看成一次简单粗暴的翻译。 什么是WSGI WSGI的全称是Web Server Gateway Interface,这是一个规范,描述了web server如何与web application...

    mod_wsgi3.5(按照apache版本,python版本,系统版本选择使用)

    这意味着用户需要确保他们下载和使用的mod_wsgi版本与他们的Apache服务器和Python环境相匹配,这样才能成功部署基于Python(如Django)的Web应用程序。 **知识点详解** 1. **mod_wsgi**: mod_wsgi是Apache的一个...

    Python-Zappa极大的简化了在AWSLambdaAPI网关上发布所有PythonWSGI应用

    Python-Zappa 是一个开源工具,它为 Python 开发者提供了一种极其便捷的方式,将基于 WSGI(Web Server Gateway Interface)的 Python Web 应用部署到 AWS Lambda 和 API Gateway 上,实现“无服务器”架构。...

    Python 200行代码实现简单的WebServer,还支持图片加载源码WSGI

    本主题将深入探讨如何使用Python编写一个简单的Web服务器,该服务器还具备加载图片的功能,并基于WSGI(Web Server Gateway Interface)协议。首先,让我们理解WSGI的基本概念。 WSGI是Python Web应用的标准接口,...

    Python-AtinyWSGIwebframework一个WSGIWeb框架Python35

    Python-AtinyWSGIwebframework是一个基于Python 3.5的微型WSGI(Web Server Gateway Interface)Web框架。WSGI是Python中定义的一种标准接口,它使得Web应用和Web服务器之间能够有效地通信。这个框架的设计目标是...

    详解如何在Apache中运行Python WSGI应用

    如果我们的Web应用是采用Python开发,而且符合WSGI规范,比如基于Django,Flask等框架,那如何将其部署在Apache中呢?本文中,我们就会介绍如何使用Apache模块mod_wsgi来运行Python WSGI应用。 安装mod_wsgi 我们...

    python中WSGI是什么,Python应用WSGI详解

    为了让大家更好的对python中WSGI有更好的理解,我们先从最简单的认识WSGI着手,然后介绍一下WSGI几个经常使用到的接口,了解基本的用法和功能,最后,我们通过实例了解一下WSGI在实际项目中如何使用。 WSGI是什么? ...

    mod_wsgi python开发

    **正文** `mod_wsgi` 是一款用于在Apache Web服务器上部署和运行Python Web应用程序的模块,它实现了WSGI(Web...通过熟练掌握`mod_wsgi`的使用,开发者可以构建高效、稳定的Python Web应用,并充分利用Apache的特性。

    python项目练习-python3实现web服务器

    8. **扩展性**:虽然这个项目仅涉及基础Web服务器的实现,但了解如何扩展到更复杂的服务,比如使用WSGI(Web Server Gateway Interface)与Flask、Django等Web框架集成,或者通过多线程或异步I/O来处理并发请求,都...

    django+apache+mod_wsgi完美匹配包

    而mod_wsgi则是Apache的一个模块,用于在Apache服务器上运行Python Web应用程序,如Django。这个完美匹配包旨在帮助用户轻松地在Apache服务器上部署Django项目。 **Django框架** Django的核心特性包括MVC(模型-...

    python+wsgi+apache

    python2.7+Django 1.11 在windows 下部署到apache24 下可用

    apache2.2和python2.7的mod_wsgi

    Apache 2.2 和 Python 2.7 之间的交互主要依赖于一个名为 mod_wsgi 的模块,它是 Apache HTTP 服务器的一个扩展,允许在 Apache 上运行 Python Web 应用程序,特别是像 Django 这样的高级 Web 框架。在本场景中,...

    Python Web开发实战_python工程_python_web_

    Python Web开发是现代互联网行业中一个不可或缺的技能,尤其对于前端工程师而言,理解并掌握Python Web开发技术至关重要。本文将深入探讨Python在Web开发中的应用,包括基础概念、常用框架、开发流程以及实战技巧。 ...

    mod_wsgi-3.5

    **标题:“mod_wsgi-3.5”** **描述**:`mod_wsgi`是...总结来说,“mod_wsgi-3.5”是Apache服务器上用于部署和运行Python WSGI应用的重要组件,它的版本特性、兼容性和配置方法是理解其工作原理和有效使用的关键。

    Python-通过Django在web上实现xshell的功能

    Django是Python中广泛使用的Web开发框架,它提供了高效、简洁且实用的工具,用于构建高质量的Web应用。本项目的目标是利用Django来构建一个类似xshell的Web应用,允许用户在浏览器中执行远程服务器的命令,实现安全...

    Python学习笔记-WSGI接口

    在Python web开发中,WSGI(Web Server Gateway Interface)是一种标准接口,用于web服务器与web应用之间的通信。这个接口定义了一种规范,使得不同的服务器和应用程序可以协同工作,提高了代码的可移植性和灵活性。...

Global site tag (gtag.js) - Google Analytics