Как получить полное ответное сообщение сервера в запросах (или urllib3.response)? - PullRequest
0 голосов
/ 17 сентября 2018

В эти несколько дней я столкнулся с проблемами с Google Sheet API, все ответные сообщения, которые я могу получить из Requests (в виде расширения request_oauthlib), это заголовки & 400 Bad Request. Запрашивает версию 2.19.1. Это произошло с несколькими конечными точками (v4), такими как spreadsheets.values.append, spreadsheets.values.batchUpdate, spreadsheets.values.update и т. Д.

Я потратил часы, чтобы убедиться, что сформировал правильную структуру данных безрезультатно, и только тогда я понял, что не получил полное ответное сообщение от сервера API.

Кажется, поскольку requests.Response объект, который возвращается из r = request.Request('https://httpbin.org'), является объектом requests.HTTPAdapter, который, в свою очередь, является своего рода оболочкой для urllib3.response.HTTPResponse. Поэтому я попытался использовать r.reason, r.raw._original_response, r.raw._body, r.raw.msg, которые не дали мне ничего, кроме ссылки на объект & None (или просто пустую строку).

Короче говоря, я сдался на некоторое время, и когда вернулся к нему, я понял, что должен был попытаться использовать проводник API Google, который я игнорировал, и оказалось, что это сообщение об ошибке в ответе, и ошибка 400 не была вызвана неправильной формой полезной нагрузки / структуры данных.

{   "error": {
    "code": 400,
    "message": "Invalid data[0]: Requested writing within range [mysheet!A3:AH3], but tried writing to row [4]",
    "status": "INVALID_ARGUMENT"   } 
}

Мой вопрос:

Был ли я неправ в том, как я отлаживаю ответ соединения на запросы, и на самом деле есть способ получить полное необработанное сообщение от сервера в запросах, как режим полной отладки в curl? Или это сообщение на самом деле отправляется только в том случае, если запрос API поступает из Google API Explorer?

Ответы [ 2 ]

0 голосов
/ 18 сентября 2018

Сообщение об ошибке, которое вы ищете, находится в теле ответа. Доступ к этому можно получить с помощью response.text (или response.json(), так как конкретное тело - JSON). Если, как предполагает ваш самоответ, вы хотите получить тело из исключения, увеличенного на raise_for_status(), вы можете просто пройти через атрибут response исключения: exception.response.text или exception.response.json().

0 голосов
/ 18 сентября 2018

Я обнаружил закрытую проблему в репозитории запросов с прошлого года под названием «Предложение: улучшить повышение статуса, чтобы включить тело ответа, если оно есть», выданное westover , в который он написал:

Требуется более подробная информация об исключении HTTPError. API, с которым я работаю, отправляет обратно 400, но в нем также есть текстовое / json тело. Было бы неплохо, если бы функция lift_for_status () включала текст ответа, если он был.

Он также предоставил небольшую обходную функцию, которая стала основой причины, по которой обсуждение перешло в обсуждение запроса на извлечение :

import requests
from requests.exceptions import HTTPError
def expanded_raise_for_status(res):
    """
    Take a "requests" response object and expand the raise_for_status method to return more helpful errors
    @param res:
    @return: None
    """
    try:
        res.raise_for_status()
    except HTTPError as e:
        if res.text:
            raise HTTPError('{} Error Message: {}'.format(str(e.message), res.text))
        else:
           raise e
    return

Я принял это в свой существующий класс и, по сути, jwodder также указал, что он расширяет / перехватывает Response.raise_for_status() метод, который вызывает HTTPError(RequestsException) класс всякий раз, когда используется raise_for_status() пользователями, чтобы подать сообщение об ошибке из ответа сервера, как правило, в виде assert response.status_code == 200, raise_for_status(). Это достигается путем добавления Response.text (или content / json методов) к сообщению об исключении и возвращает тело ответа вместо текущего кода возврата <self.status_code, self.reason, self.url>, который переводится в 400 Bad Requests <url>.

def raise_for_status(self):
    """Raises stored :class:`HTTPError`, if one occurred."""

    http_error_msg = ''
    if isinstance(self.reason, bytes):
        # We attempt to decode utf-8 first because some servers
        # choose to localize their reason strings. If the string
        # isn't utf-8, we fall back to iso-8859-1 for all other
        # encodings. (See PR #3538)
        try:
            reason = self.reason.decode('utf-8')
        except UnicodeDecodeError:
            reason = self.reason.decode('iso-8859-1')
    else:
        reason = self.reason

    if 400 <= self.status_code < 500:
        http_error_msg = u'%s Client Error: %s for url: %s' % (self.status_code, reason, self.url)

    elif 500 <= self.status_code < 600:
        http_error_msg = u'%s Server Error: %s for url: %s' % (self.status_code, reason, self.url)

    if http_error_msg:
        raise HTTPError(http_error_msg, response=self)

этом ответе он далее изложил причину, по которой он считает, что она заслуживает особой возможности, но запрос на получение ответа был отклонен, и я думаю, что сопровождающий также имел хорошие моменты), это может привести к другим исключения или более осложнения, если не использовать вдумчиво / экономно, учитывая популярность модуля и широкие причины его использования.

...