获取 POST 请求体
21 世纪的 Web 交互中服务器跟浏览器互相丢 JSON 已经成了司空见惯的事情. 在 Flask 框架作成的服务器上要搞到 JSON 数据当然是直接访问 POST 请求体了, 如import flask
import functools
app = flask.Flask(__name__)
@app.route('/wtf', methods=['POST'])
def wtf():
return 'Received: ' + flask.request.data
def main():
app.run(port=7777)
if __name__ == '__main__': main()
flask.request.data
包含请求数据字符串. 但其实这也是个坑, 默认情况下根本取不到请求数据$ curl -d "[1,1,2,3,5,8]" http://localhost:7777/wtf
Received:
- Contains the incoming request data as string in case it came with a mimetype Flask does not handle.
flask.request.json
, 但这货一般是 None
, 只有当请求 mimetype 被设置为 application/json 的时候才有用, Flask 你真心是跟 mimetype 过不去啊. 也就是说得这样发请求$ curl -d "[1,1,2,3,5,8]" localhost:7777/wtf
Received: null
$ curl -d "[1,1,2,3,5,8]" -H "Content-Type:application/json" localhost:7777/wtf
Received: [1, 1, 2, 3, 5, 8]
# Server codes
import json
import flask
import functools
app = flask.Flask(__name__)
@app.route('/wtf', methods=['POST'])
def wtf():
return 'Received: ' + json.dumps(flask.request.json)
def main():
app.run(port=7777)
if __name__ == '__main__': main()
好吧, 如果实在不行, 就挖到 WSGI 里面去好了, 比如这样
def request_data():
d = flask.request.data
if not d:
return ''.join(flask.request.environ['wsgi.input'].readlines())
return d
装饰器对被装饰函数的名字是敏感的 [已修正]
Flask 更新到 0.10 之后以下问题已经修正. 现在定义同名的请求处理函数会直接报错, 程序退出.如官网上的例子
import flask
app = flask.Flask(__name__)
@app.route('/')
def hello():
return 'Hello World'
if __name__ == '__main__': app.run(port=7777)
import flask
app = flask.Flask(__name__)
app.route('/')(lambda: 'Hello World')
if __name__ == '__main__': app.run(port=7777)
import flask
app = flask.Flask(__name__)
app.route('/')(lambda: 'Hello World')
app.route('/wtf')(lambda: 'I bought a watch last year')
if __name__ == '__main__': app.run(port=7777)
$ curl http://localhost:7777/
I bought a watch last year
$ curl http://localhost:7777/wtf
I bought a watch last year
查了一通文档才发现 Flask 会索引被装饰函数的名字, 并根据这些名字来派发请求. 好了, 结论是不要在 Flask 里玩 lambda, 因为 Python 里面所有 lambda 名字都一个样
>>> x = lambda : None
>>> x.__name__
'<lambda>'
>>> y = lambda m, n: m + n
>>> y.__name__
'<lambda>'
import flask
app = flask.Flask(__name__)
def handler_logger(f):
def g(*args, **kwargs):
print 'before request'
r = f(*args, **kwargs)
print 'after request'
return r
return g
@app.route('/')
@handler_logger
def root():
return 'Hello World'
@app.route('/wtf')
@handler_logger
def wtf():
return 'I bought a watch last year'
if __name__ == '__main__': app.run(port=7777)
刚才已经说了 Flask 对被包装函数的名字是敏感的, 所以解决上述问题的方法便是
import flask
import functools
app = flask.Flask(__name__)
def handler_logger(f):
@functools.wraps(f)
def g(*args, **kwargs):
print 'before request'
r = f(*args, **kwargs)
print 'after request'
return r
return g
@app.route('/')
@handler_logger
def root():
return 'Hello World'
@app.route('/wtf')
@handler_logger
def wtf():
return 'I bought a watch last year'
def main():
app.run(port=7777)
if __name__ == '__main__': main()