这是一个可以从命令行或通过Apache / WSGI运行的Flask应用程序:
import flask
app = flask.Flask(__name__)
LENGTH = 1000000 # one million
@app.route('/', methods=['HEAD'])
def head():
return 'x' * LENGTH # response body isn't actually sent
@app.route('/', methods=['GET'])
def get():
import random
return ''.join(str(random.randint(0,9)) for x in range(LENGTH))
if __name__ == '__main__':
app.run() # from command-line
else:
application = app # via Apache and WSGI
即,此应用程序返回一百万个随机数字。 GET请求需要花费很多时间,但是HEAD请求应该能够立即返回。这当然是一个说明性的例子;实际应用程序将涉及对GET请求生成缓慢的大响应,但也具有可由HEAD请求快速查询的预定大小。 (另一种情况:我尝试将请求重定向到预先签名的Amazon S3 URL,对于HEAD和GET方法,这些URL必须以不同方式签名。)
问题#1)当我从命令行运行Flask应用程序时,HEAD请求会按预期激活head
函数;但是当我通过Apache / WSGI运行它时,它会激活get
函数。为什么会这样,我怎样才能解决这个问题?
问题#2)为什么我不能为HEAD请求创建虚拟响应(分配一堆内存)?
我的猜测是,这些都是出于善意的尝试,以确保HEAD请求始终与相应的GET一致。所以:
猜猜#1)Apache或WSGI都在内部重写HEAD到GET。
猜猜#2)Flask并不信任我手动设置Content-Length标头,并用响应体的实际长度重写它......即使是HEAD请求,其实际上应该是是空的。
我误解了什么吗?关于如何能够更快地处理HEAD请求的任何建议,理想情况下无需慢慢生成仅用于设置Content-Length标头的大型响应主体?
答案 0 :(得分:3)
正如已经指出的那样,mod_wsgi将HEAD重新映射到GET的问题在以下内容中有详细描述:
特别是,如该博客文章中所述,如果你有一个Apache输出过滤器设置,那么它有可能需要从你的WSGI应用程序看到针对相同URL的GET或HEAD的相同输出,然后mod_wsgi不会相信你的应用程序做了正确的事情并将HEAD重新映射到GET以确保Apache输出过滤器能够正常工作。
如果你不关心你没有为HEAD请求返回与GET请求相同的响应头,从而打破了HTTP RFC指定的HEAD要求,那么只需确保你没有Apache配置了输出过滤器,你可以随意破解,因为mod_wsgi不会重新映射请求方法类型。
答案 1 :(得分:2)
要从Flask创建完整的回复,您需要执行以下操作:
@app.route('/', methods=['HEAD'])
def head():
response = Response()
response.headers.add('content-length', LENGTH)
return response
然后会产生这样的结果:
Connected to localhost.
Escape character is '^]'.
HEAD / HTTP/1.1
Host: localhost
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
content-length: 1000000
Server: Werkzeug/0.9.4 Python/2.7.6
Date: Sun, 16 Mar 2014 22:59:16 GMT
仅使用标准跑步者测试,而不是通过wsgi,但它不应该有所作为。
对于强制使用get处理程序的Apache / WSGI,this blog entry提供了一些关于为什么会发生这种情况的提示。
请参阅:Flask/Werkzeug how to attach HTTP content-length header to file download