2007年03月02日

今天在djangosnippets.com 上提交了两个Middleware,一个是 ProfileMiddleware,它可以对每个请求记录profile信息,用来进行性能测试。另一个是FormatMiddleware,它可以自动根据上传的QueryString或POST数据中是否有format信息来自动进行格式转换,我想这个会非常方便的。

这里我想主要说一下FormatMiddleware的作用。在页面上我倒是用英文写了不少的说明。其主要的想法就是:

在许多时候,一个View的处理在不同的情况下希望可以有不同的输出。比如希望可以输出为Html,json格式,或者支持xml-rpc的处理。那么通常情况下我们可以通过decorator来实现这一点,但是一次只能使用一种格式。如果我希望同时支持多种格式怎么办?因此我开发了这个Middleware。你可以在你的请求中加入格式要求的信息,如:

http://localhost:8000/booklist/ajax_list/?format=xmlrpc

可以看到,后面有一个format=xmlrpc的信息,它表明为需要xmlrpc的格式。如果把format改为json,说明它需要json格式。这样后台的一个view可以只输出真正的数据,然后在Middleware中,根据要求格式的不同,自动转换为相应的格式。对于json相对简单,但对于xmlrpc要复杂一些,特别是对于请求的处理。一般的xml-rpc都是联到服务器,然后调用方法,传入参数。这样方法名与链接可能是没有直接的关系。但这样会造成一个view方法不方便支持xmlrpc的处理。因此我采用服务器的地址为url的方式,然后将传入的参数转为POST的数据,这样就可以象一般的web请求一样来处理xmlrpc的请求了。只不过对于xmlrpc的方法不支持参数key值,只能传入参数值,也就是不支持:func(name=value)的形式,只能使用func(value),所以我改为传入一个dict,这样在Middleware中将这个dict自动转为POST数据。

对于html也有特殊处理。因为html一般与某个模板相关联,因此需要把模板名保存到一个地方,在真正需要html格式时才进行处理。因此在这个Middleware中判断了request.format_processor这个属性,如果有,则用它来进行渲染处理,为了更加通用,它只是一个可执行对象,接受一个数据的参数。因此你可以加入自已的处理器。当然具体的例子还没有,它可以认为是一个微型框架,你可以在view方法中进行注入,或写一个decorator来处理request对象,如:

request.format = ‘html’
request.format_processor = template_func(request, ‘users/html’)

其中template_func应该是一个decorator函数,它返回一个可处理数据的函数。

通过Middleware的方式,可以使许多处理自动化,当然也造成必须要遵守许多的约定。比如FormatMiddleware必须要安装在所有Middleware的后面,以保存在返回时它是第一个处理。为什么要这样,因为我查阅了django的源码,在处理Middleware时,django是按顺序来处理的,对于process_request,它是按配置的顺序来调用的。对于process_response,它是倒序来执行的,因此与配置的顺序正好相反。而如果你的view方法返回的不是一个真正的HttpResponse时,在其它的Middleware处理时很可能会报错。所以要求FormatMiddleware先执行。这是一个需要注意的地方。这些都是看源码才会清楚的东西。