Возврат объекта 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. Я пытался вернуться resprequests.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()))
Другие вопросы по тегам