WSGI(5)-中间件实现示例

这里通过几种不同应用场景来说明中间件的一些应用。

路由中间件

比如某些情况下面我们需要将不同的app一起调度,比如一个应用的api接口和后台管理页面分为两个app开发。当然路由的实现可以通过很多方式,比如flask中起不同的路由,或者通过nginx做代理。这只是一种解决方式。
其应用代码如下:

1
2
3
4
5
6
7
8
9
10
11
from testapi import appapi
from testapp import app

def route_middleware(environ, start_response):
path = environ.get("PATH_INFO")
if path.startswith("/api"):
return appapi(environ, start_response)
else:
return app(environ, start_response)

application = make_server(route_middleware)

这里通过实现接受environ和start_response的接口来实现路由中间件功能。
在flask中就有这种中间件提供:

1
2
3
4
5
6
7
8
9
from werkzeug.wsgi import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app1 import application1 as backend1
from backend_app2 import application2 as backend2

application = DispatcherMiddleware(frontend, {
'/backend1': backend1,
'/backend2': backend2,
})

具体可以看这里 http://docs.jinkan.org/docs/flask/patterns/appdispatch.html

结果处理中间件

对结果进行处理,比如将返回的字符串都变为小写。

1
2
3
4
5
6
7
8
9
10
11
class UpperMiddleware:
def __init__(self, app):
self.app = app

def __call__(self, environ, start_response):
for data in self.app(environ, start_response):
return data.upper()

application = Upperware(simple_app)
server = make_server('127.0.0.1', 7000, application)
server.serve_forever()

webob编写中间件

以上两个可以看到一个是服务器到应用的中间件处理,一个是应用返回给服务器的处理,中间件可以在不同数据流向上存在。上面两种也分别是通过函数和类实现,都需要符合wsgi接口规范的实现。除了以上通过简单的编写中间件接口以外还可以基于python的webob(WSGI request and response objects)库来编写。webob让封装了WSGI的处理,以及response消息处理,从而能够比较容易创建中间件。下面给出代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# coding=utf-8

import webob
import webob.dec
from wsgiref.simple_server import make_server


@webob.dec.wsgify
def test_middle1(req):
print 'test_middle1'
return test_middle2


@webob.dec.wsgify
def test_middle2(req):
print 'test_middle2'
return test_app
# return webob_app


@webob.dec.wsgify
def webob_app(req):
print 'test_webob'
return webob.Response('hello webob\n')


def test_app(environ, start_response):
response_headers = [('Content-Type', 'text/html')]
status = '200 OK'
start_response(status, response_headers)
return ["<h1>hello world</h1>\n"]


if __name__ == '__main__':
server = make_server('', 7000, test_middle1)
server.serve_forever()

代码中test_middle1、test_middle2、webob_app都是基于webob.dec.wsgify写的,从这里可以看出webob.dec.wsgify都可以互相调用,也可以调用其它app。在test_middle2中可以调用test_app,通过return即可。这里和上面的中间件例子有点区别,中间件例子中是直接调用了app,然后返回了app调用后的结果,这里是直接return了这个app或者middle,由webob来处理,看起来要简洁。下面通过调用test_app的结果,可以看到依次调用了test_middle1,test_middle2和test_app,返回了hello world。


下面是调用webob_app的结果,分别调用了test_middle1、test_middle2、test_webob,然后输出了hello webob


通过webob.dec.wsgiref装饰后类似于发生了一下动作

1
webob_app = wsgify(webob_app)

而这里webob_app可以接收参数environ和start_response,即

1
webob_app(environ, tart_response)

webob具体原理可以通过以下地方去了解:
http://gtcsq.readthedocs.io/en/latest/py_doc/webob_analysis.html