Flask 如何获取请求的body

flask request body


1、 body

 

一个http 请求分为两部分,第一部分是首部,也就是编程语言中的headers,另一个部分是body,是请求的数据部分,两部分用\r\n\r\n分割。通常,只有在post请求中才会有body,因为其他请求通常不会向服务器提交数据,但如果get请求里有body数据,http协议也并没有禁止这种行为。

各种web框架都允许编程人员在获取request中的body部分,但是flask却有一点点不同


 

2、 Flask 请求中的body

 

你可能会查到使用 request.get_data()  来获取body,但是这并不总是成功,很悲剧

之所以不成功,是因为你在使用 request.get_data() 之前,已经通过其他手段获取了body被解析后的数据,例如   request.files['file'], 这是获得上传文件的方法,如果这一句在get_data()前面,那么你在使用request.get_data() 时就无法获得数据

查看一下request.files 源码, 它会调用  _load_form_data,而这个方法的注释如下

 

"""Method used internally to retrieve submitted data.  After calling
        this sets `form` and `files` on the request object to multi dicts
        filled with the incoming form data.  As a matter of fact the input
        stream will be empty afterwards.  You can also call this method to
        force the parsing of the form data.

        .. versionadded:: 0.8
"""

注释说的很清楚,如果调用了该方法,那么form 和files 集合将被body解析后的数据填充,而stream将会被清空,因为数据已经从数据流中读取出来并且被解析了,因此,没必要再存储stream了,但是request.get_data()  却偏偏是要从stream读取数据,看代码

def get_data(self, cache=True, as_text=False, parse_form_data=False):
        rv = getattr(self, '_cached_data', None)
        if rv is None:
            if parse_form_data:
                self._load_form_data()
            rv = self.stream.read()
            if cache:
                self._cached_data = rv
        if as_text:
            rv = rv.decode(self.charset, self.encoding_errors)
        return rv

这样一来,get_data()就无法获得数据

 

如果想稳定的获得body数据,那么就一定要先调用get_data()方法,这样,既可以获得body数据,也不影响request.form 和 request.files的使用


 

3、 一个更优雅的实现

 

虽然首先调用get_data()方法可以获得body,但总是有些被限制的赶脚,在 stackoverflow 上找到了一个非常优雅的实现方式

class WSGICopyBody(object):
    def __init__(self, application):
        self.application = application

    def __call__(self, environ, start_response):

        from cStringIO import StringIO
        length = environ.get('CONTENT_LENGTH', '0')
        length = 0 if length == '' else int(length)

        body = environ['wsgi.input'].read(length)
        environ['body_copy'] = body
        environ['wsgi.input'] = StringIO(body)

        # Call the wrapped application
        app_iter = self.application(environ,
                                    self._sr_callback(start_response))

        # Return modified response
        return app_iter

    def _sr_callback(self, start_response):
        def callback(status, headers, exc_info=None):

            # Call upstream start_response
            start_response(status, headers, exc_info)
        return callback

app = Flask(__name__)
app.wsgi_app = WSGICopyBody(app.wsgi_app)

 

这里定义了一个名为 WSGICopyBody 的类,用它对app.wsgi_app 进行一层包装,将得到的body数据存放在environ['body_copy']  中

这样,以后想使用的时候,直接获取即可  

request.environ['body_copy']

博文最后更新时间:

博客统计

访问量:396588

博文总数:300 评论总数:0

原创298 转载2 翻译0

交流学习

    加QQ群: 211426309 ,一起学习进步