我一直试图从语言的角度理解Flask装饰器。基于我对装饰器的理解,method1,method2和method3应该具有相同的功能。
# server.py
#
# callback-based server response, based on official Flask docs:
# http://flask.pocoo.org/snippets/8/
#
# to run server:
# gunicorn server:app --bind localhost:5000
#
# to test:
# curl http://localhost:5000/1 -X POST -d POST_DATA -H "Content-Type: application/json"
# curl http://localhost:5000/2 -X POST -d POST_DATA -H "Content-Type: application/json"
# curl http://localhost:5000/3 -X POST -d POST_DATA -H "Content-Type: application/json"
from flask import Flask, request
app = Flask(__name__)
def callback(*args, **kwargs):
print "post_data=", request.data
return "RESPONSE\n", 200
def wrapper(f):
return callback
# method 1
@app.route('/1', methods=['POST'])
@wrapper
def method1():
pass
# method 2
@app.route("/2", methods=['POST'])
def method2():
return wrapper(method2)
# method 3
@app.route("/3", methods=['POST'])
def method3():
return callback
但是,当我进行3次卷曲测试时,结果会有所不同。在/ 1和/ 2的情况下,程序在打印“post_data = POST_DATA”后不久崩溃:
post_data= POST_DATA
[2017-08-03 15:20:26,625] ERROR in app: Exception on /3 [POST]
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1615, in full_dispatch_request
return self.finalize_request(rv)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1630, in finalize_request
response = self.make_response(rv)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1740, in make_response
rv = self.response_class.force_type(rv, request.environ)
File "/usr/local/lib/python2.7/dist-packages/werkzeug/wrappers.py", line 885, in force_type
response = BaseResponse(*_run_wsgi_app(response, environ))
File "/usr/local/lib/python2.7/dist-packages/werkzeug/test.py", line 903, in run_wsgi_app
buffer.append(next(app_iter))
StopIteration
请注意,在完全退出app.route方法后,它在Flask中死了。
一些基础研究表明其他人遇到过这种“StopIteration”问题: https://github.com/getsentry/raven-python/issues/514 但不是在这样一个简单的例子中,也不是在使用官方烧瓶文档(http://flask.pocoo.org/snippets/8/)上的响应回调模式的任何示例中。因此我得出结论,问题不在于Flask本身,而在于我对装饰器的理解。
我不明白这3种方法可能会做出不同的事情。所有这些都引用了完全相同的“回调”功能。要么他们成功地将这个功能提供给Flask,要么他们没有。
如果他们确实将功能提供给Flask ...则测试成功。 如果他们没有将功能提供给Flask ......那么Flask如何调用函数来打印“post_data = POST_DATA”?
这种行为似乎完全不合逻辑。
(或者我误解了装饰器的作用,实际上它将某种隐藏的元数据放入结果中,Flask是邪恶的,并以未记录的方式检查元数据。)
答案 0 :(得分:1)
您的代码存在基本问题。首先,回想一下装饰者......
@decorator
def myfunction():
pass
......完全等同于:
def _myfunction():
pass
myfunction = decorator(_myfunction)
考虑到这一点,您将注意到在方法2中,您将返回一个函数,而不是迭代器:
@app.route("/2", methods=['POST'])
def method2():
return wrapper(method2)
请记住,wrapper
的返回值是函数:
def wrapper(f):
return callback
所以在这里你有效地做了:
@app.route("/2", methods=['POST'])
def method2():
return callback
您将返回单个值(函数)而不是元组。你真的想要:
@app.route("/2", methods=['POST'])
def method2():
return wrapper(method2)()
类似于方法3:
@app.route("/3", methods=['POST'])
def method3():
return callback()
在这两种情况下,您都需要实际调用 callback
函数。