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或者相似的数据库迁移库。悦德财富:https://yuedecaifu.com
当然,对于非专业的应用,你也可能想使用一下框架,那样的话,一些特殊的诸如此类的例子也可以得到合理解决。
关于服务器呢?
有许许多多服务WSGI应用的方法。我们已经讨论过Gunicorn了,一个相当好的选择。uWSGI是另外一个不错的选择。但是请确保在服务这些静态东西之前设置好nginx一类的东西,并且你应该有一个固定的开始节点。
Python Web 应用:WSGI基础的更多相关文章
- python web应用--WSGI接口(二)
WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求.我们来看一个最简单的Web版本的“Hello, web!”: 1 # server.py 2 # 从wsgiref模 ...
- python Web中WSGI uWSGI 以及 uwsgi的区别
WSGI协议 首先弄清下面几个概念: WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web ...
- [Python web开发] Web框架开发基础 (一)
Python WEB框架 WSGI,WEB Server Gateway Interface,可以看做是一种底层协议,它规定了服务器程序和应用程序各自实现上面接口.Python的实现称为wsgiref ...
- 【Python Programe】WSGI (Web Server Gateway Interface)
Part1: What is a Web server? 一个位于物理服务器上的网络服务器(服务器里的服务器),等待客户端去发送request,当服务器接收到request,就会生成一个respons ...
- django基础一:web、wsgi、mvc、mtv
一.web框架 web框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以快速开发特定的系统.他山之石,可以攻玉.python的所有web框架,都是对so ...
- 戏说WSGI(Python Web服务网关接口)--[转载]
戏说WSGI(Python Web服务网关接口) 当你在Python的世界中冒险,突然遭遇一只Web怪兽,你会选择什么武器对付它?在兵器谱上,下列兵器可谓名列前茅: Zope,厚重的长枪.较早出现的武 ...
- Python Web Server Gateway Interface -- WSGI
了解了HTTP协议和HTML文档,我们其实就明白了一个Web应用的本质就是: 浏览器发送一个HTTP请求: 服务器收到请求,生成一个HTML文档: 服务器把HTML文档作为HTTP响应的Body发送给 ...
- [Python WEB开发] 使用WSGI开发类Flask框架 (二)
WSGI Web服务器网关接口 WSGI主要规定了Web服务器如何与Web应用程序进行通信,以及如何将Web应用程序链接在一起来处理一个请求. wsgiref Python中的WSGI参考模块 ...
- python web框架 django wsgi 理论
django wsgi python有个自带的wsgi模块 可以写自定义web框架 用wsgi在内部创建socket对象就可以了 自己只写处理函数就可以了django只是web框架 他也不负责写soc ...
随机推荐
- mysql 保留两位小数
mysql保留字段小数点后两位小数用函数:truncate(s.price,2)即可.如果想用四舍五入的话用round(s.price,2).
- weblogic 下载安装部署说明
http://blog.csdn.net/gaofuqi/article/details/36870887/ http://jingyan.baidu.com/article/d8072ac45f57 ...
- 【CITE】C#入门学习-----简单画图程序
版权声明:本文为博主原创文章,未经博主允许不得转载. 欢迎大家提出意见,一起讨论! 转载请标明是引用于 http://blog.csdn.net/chenyujing1234 通过本实例了解如何在窗体 ...
- angular--todomvc
类名:ng-class="{completed:todo.completed}" ng-repeat="todo in todoData" ng-model=& ...
- c# BackGroundWorker 多线程操作的小例子
在我们的程序中,经常会有一些耗时较长的运算,为了保证用户体验,不引起界面不响应,我们一般会采用多线程操作,让耗时操作在后台完成,完成后再进行处理或给出提示,在运行中,也会时时去刷新界面上的进度条等显示 ...
- javascript实现对象的继承的方式
在JavaScript将原型链作为实现继承的主要方法.基本原理是利用原型让一个subType引用superType的属性和方法 推荐链接 http://www.jb51.net/article/204 ...
- Permutations [LeetCode]
Given a collection of numbers, return all possible permutations. For example,[1,2,3] have the follow ...
- PHP FTP操作类( 上传、拷贝、移动、删除文件/创建目录 )
/** * 作用:FTP操作类( 拷贝.移动.删除文件/创建目录 ) * 时间:2006/5/9 * 作者:欣然随风 * QQ:276624915 */ class class_ftp { publi ...
- 关于linux服务器的批量维护、批量升级、
由于版权费用原因,众多中小服务器采用linux服务器进行功能处理.在进行批量升级.更新业务程序时.可以采用如下批量方案: 1.打包成rpm包,并配置yum源服务器,以支持个人的yum服务器进行处理.一 ...
- sql取年月日
Sql Server 中一个非常强大的日期格式化函数 Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2006 10:57AM Select CON ...