简体   繁体   English

从 Flask 返回 requests.Response 对象

[英]Return a requests.Response object from Flask

I'm trying to build a simple proxy using Flask and requests.我正在尝试使用 Flask 和请求构建一个简单的代理。 The code is as follows:代码如下:

@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

This works, except that it loses the status code from Elasticsearch.这可行,只是它会丢失 Elasticsearch 的状态代码。 I tried returning resp (a requests.models.Response ) directly, but this fails with我尝试直接返回resp (a requests.models.Response ),但这失败了

TypeError: 'Response' object is not callable

Is there another, simple, way to return a requests.models.Response from Flask?还有另一种简单的方法从 Flask 返回requests.models.Response吗?

Ok, found it:好的,找到了:

If a tuple is returned the items in the tuple can provide extra information.如果返回元组,则元组中的项目可以提供额外信息。 Such tuples have to be in the form (response, status, headers).这样的元组必须采用(响应、状态、标题)的形式。 The status value will override the status code and headers can be a list or dictionary of additional header values.状态值将覆盖状态代码,并且标头可以是附加标头值的列表或字典。

( Flask docs .) 烧瓶文档。)

So所以

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

seems to do the trick.似乎可以解决问题。

Using text or content property of the Response object will not work if the server returns encoded data (such as content-encoding: gzip ) and you return the headers unchanged.如果服务器返回编码数据(例如content-encoding: gzip )并且您返回未更改的标头,则使用Response对象的textcontent属性将不起作用。 This happens because text and content have been decoded, so there will be a mismatch between the header-reported encoding and the actual encoding.发生这种情况是因为textcontent已被解码,因此标头报告的编码与实际编码之间存在不匹配。

According to the documentation :根据文档

In the rare case that you'd like to get the raw socket response from the server, you can access r.raw .在极少数情况下,您希望从服务器获取原始套接字响应,您可以访问r.raw If you want to do this, make sure you set stream=True in your initial request.如果您想这样做,请确保在您的初始请求中设置stream=True

and

Response.raw is a raw stream of bytes – it does not transform the response content. Response.raw是一个原始的字节流——它不会转换响应内容。

So, the following works for gzipped data too:因此,以下内容也适用于压缩数据:

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()

If you use a shortcut method such as get , it's just:如果您使用诸如get之类的快捷方法,那就是:

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

Flask can return an object of type flask.wrappers.Response . Flask 可以返回一个flask.wrappers.Response类型的对象。

You can create one of these from your requests.models.Response object r like this:您可以像这样从您的requests.models.Response对象r创建其中之一:

from flask import Response

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

I ran into the same scenario, except that in my case my requests.models.Response contained an attachment.我遇到了同样的情况,除了在我的情况下,我的 requests.models.Response 包含一个附件。 This is how I got it to work:这就是我让它工作的方式:

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

My use case is to call another API in my own Flask API.我的用例是在我自己的 Flask API 中调用另一个 API。 I'm just propagating unsuccessful requests.get calls through my Flask response.我只是通过我的 Flask 响应传播不成功的requests.get调用。 Here's my successful approach:这是我成功的方法:

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()))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM