Django与mod_wsgi及gzip
我正在使用Django作为一个REST服务器。我需要接收一个包含JSON数据的POST请求,并对其进行解析。客户端是一个Salesforce服务器,它对请求进行了gzip压缩。
为了让请求能够正确解压,我在虚拟主机配置中使用了这个设置: SetInputFilter DEFLATE
几乎一切看起来都正常,但当我读取request.body或使用request.read(16000)时——输入的内容相对较小——我总是发现响应被截断了(少了5个字符)。
有没有什么建议可以帮助我开始调试这个问题?
1 个回答
从技术上讲,WSGI规范并不支持将输入过滤器作为中间件进行修改,甚至在底层的网络服务器中也不支持。
具体问题在于,修改输入的过滤器会改变请求内容的大小,但不会改变WSGI环境字典中的CONTENT_LENGTH值。
WSGI规范规定,一个有效的WSGI应用程序只能从请求内容中读取最多CONTENT_LENGTH字节的数据。因此,在请求内容被压缩的情况下,最终的请求大小可能会大于CONTENT_LENGTH所指定的大小,这样网络框架在读取所有数据之前,可能会截断请求输入。
关于这个问题的更多细节,你可以查看:
虽然有人推动对规范进行修改,但实际上并没有什么变化。
为了解决这个问题,你需要实现一个WSGI中间件,将其包裹在Django应用程序周围。如果这个中间件通过传递的头部检测到原始内容被压缩,而你知道Apache已经解压缩了它,那么它会读取所有请求内容,直到到达流的结束标记,忽略CONTENT_LENGTH,然后再将请求传递给Django。完成这一步后,它可以修改CONTENT_LENGTH,并用一个替代的流替换wsgi.input,这样就能返回已经读取的内容。
由于内容的大小可能很大且不确定,将所有内容都读入内存并不是一个好主意。因此,你可能想要一次读取一块内容,并将其写入一个临时文件。然后,wsgi.input会被替换为临时文件的打开文件句柄,而CONTENT_LENGTH则替换为文件的最终大小。
如果你在Google Groups的mod_wsgi档案中好好搜索一下,你应该能找到之前关于这个问题的讨论,甚至可能还有一些示例代码。