【问题标题】:Return a requests.Response object from Flask从 Flask 返回 requests.Response 对象
【发布时间】:2013-11-03 08:07:33
【问题描述】:

我正在尝试使用 Flask 和请求构建一个简单的代理。代码如下:

@app.route('/es/<string:index>/<string:type>/<string:id>',
           methods=['GET', 'POST', 'PUT']):
def es(index, type, id):
    elasticsearch = find_out_where_elasticsearch_lives()
    # also handle some authentication
    url = '%s%s%s%s' % (elasticsearch, index, type, id)

    esreq = requests.Request(method=request.method, url=url,
                             headers=request.headers, data=request.data)
    resp = requests.Session().send(esreq.prepare())
    return resp.text

这可行,只是它会丢失 Elasticsearch 的状态代码。我尝试直接返回resprequests.models.Response),但这失败了

TypeError: 'Response' object is not callable

还有另一种简单的方法从 Flask 返回 requests.models.Response 吗?

【问题讨论】:

    标签: python proxy flask python-requests


    【解决方案1】:

    好的,找到了:

    如果返回元组,则元组中的项目可以提供额外信息。这样的元组必须采用(响应、状态、标题)形式。状态值将覆盖状态代码,并且标头可以是附加标头值的列表或字典。

    (Flask docs.)

    所以

    return (resp.text, resp.status_code, resp.headers.items())
    

    似乎可以解决问题。

    【讨论】:

    • 使用resp.content 而不是resp.text 来处理二进制和文本响应
    • 返回值不需要加括号。
    • 我仍然收到“收到内容编码的响应:gzip,但未能解码”...任何指导?
    【解决方案2】:

    我遇到了同样的情况,除了我的 requests.models.Response 包含一个附件。这就是我让它工作的方式:

    return send_file(BytesIO(result.content), mimetype=result.headers['Content-Type'], as_attachment=True)

    【讨论】:

    • 谢谢!这也解决了我的标题问题(以某种方式造成了 CORS 问题)
    【解决方案3】:

    如果服务器返回编码数据(例如content-encoding: gzip)并且您返回的标头未更改,则使用Response 对象的textcontent 属性将不起作用。出现这种情况是因为textcontent 已被解码,因此标头报告的编码与实际编码不匹配。

    根据the documentation

    在极少数情况下,您希望从服务器获取原始套接字响应,您可以访问r.raw。如果您想这样做,请确保在初始请求中设置 stream=True

    Response.raw 是原始字节流——它不会转换响应内容。

    因此,以下内容也适用于压缩数据:

    esreq = requests.Request(method=request.method, url=url,
                             headers=request.headers, data=request.data)
    resp = requests.Session().send(esreq.prepare(), stream=True)
    return resp.raw.read(), resp.status_code, resp.headers.items()
    

    如果使用get等快捷方式,那就是:

    resp = requests.get(url, stream=True)
    return resp.raw.read(), resp.status_code, resp.headers.items()
    

    【讨论】:

    • 这是我决定的方法。但是,我发现我必须从响应标头中弹出 Transfer-Encoding 项。如果我不这样做,我会收到错误:“响应标头不能包含带有分块编码的 'Content-Length'”
    【解决方案4】:

    我的用例是在我自己的 Flask API 中调用另一个 API。我只是通过我的 Flask 响应传播不成功的 requests.get 调用。这是我成功的方法:

    headers = {
        'Authorization': 'Bearer Muh Token'
    }
    try:
        response = requests.get(
            '{domain}/users/{id}'\
                .format(domain=USERS_API_URL, id=hit['id']),
            headers=headers)
        response.raise_for_status()
    except HTTPError as err:
        logging.error(err)
        flask.abort(flask.Response(response=response.content, status=response.status_code, headers=response.headers.items()))
    

    【讨论】:

      【解决方案5】:

      Flask 可以返回 flask.wrappers.Response 类型的对象。

      您可以像这样从 requests.models.Response 对象 r 创建其中之一:

      from flask import Response
      
      return Response(
          response=r.reason,
          status=r.status_code,
          headers=dict(r.headers)
      )
      

      【讨论】:

        猜你喜欢
        • 2014-07-26
        • 2015-04-18
        • 1970-01-01
        • 2015-05-23
        • 1970-01-01
        • 1970-01-01
        • 2015-05-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多