Возврат объекта request.Response из Flask
Я пытаюсь создать простой прокси с помощью 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. Я пытался вернуться resp
(а requests.models.Response
) напрямую, но это не с
TypeError: 'Response' object is not callable
Есть ли другой, простой способ вернуть requests.models.Response
из колбы?
3 ответа
Ok, found it:
If a tuple is returned the items in the tuple can provide extra information. Такие кортежи должны быть в форме (ответ, статус, заголовки), где хотя бы один элемент должен быть в кортеже. The status value will override the status code and headers can be a list or dictionary of additional header values.
( Flask docs.)
Так
return (resp.text, resp.status_code, resp.headers.items())
кажется, делает свое дело.
С помощью text
или же content
собственность Response
Объект не будет работать, если сервер возвращает закодированные данные (например, content-encoding: gzip
) и вы возвращаете заголовки без изменений. Это происходит потому, что text
а также content
были декодированы, поэтому будет несоответствие между кодированием, сообщаемым заголовком, и фактическим кодированием.
Согласно документации:
В редком случае, когда вы хотите получить необработанный ответ сокета от сервера, вы можете получить доступ к
r.raw
, Если вы хотите сделать это, убедитесь, что вы установилиstream=True
в вашем первоначальном запросе.
а также
Response.raw
это необработанный поток байтов - он не преобразует содержимое ответа.
Итак, для gzipped-данных тоже работает следующее:
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()
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)
)
Я столкнулся с тем же сценарием, за исключением того, что в моем случае мой request.models.Response содержал вложение. Вот как я получил это на работу:
return send_file (BytesIO (result.content), mimetype = result.headers ['Content-Type'], as_attachment = True)
Мой вариант использования - вызвать другой API в моем собственном API Flask. Я просто неудачно пропагандируюrequests.get
звонит через мой ответ Flask. Вот мой успешный подход:
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()))