Python запрашивает POST делать GET?

Я использую Python 2.7.5, Django 1.7, запросы 2.4.1 и выполняю несколько простых тестов. Тем не менее, похоже, что когда я вызываю request.post, метод вместо этого выполняет GET.

Мой код, говорящий с RESTful API. Обратите внимание, что команда POST работает через Hurl.it с этой полезной нагрузкой и конечной точкой:

def add_dummy_objective(self):
    """
    To the bank
    """
    payload = {
        'displayName': {
            'text': self._test_objective
        },
        'description': {
            'text': 'For testing of API Middleman'
        },
        'genusTypeId': 'DEFAULT'
    }
    obj_url = self.host + self.bank_id + '/objectives/?proxyname=' + self._admin_key
    req = requests.post(obj_url, data=json.dumps(payload), headers=self.headers)
    return req.json()

Я устанавливаю заголовки для JSON:

self.headers = {
    'Content-Type'  : 'application/json'
}

Вместо создания новой цели (как и ожидалось с POST), я получаю список целей обратно (что я ожидал бы с GET). Используя pdb, я вижу:

(Pdb) req.request
<PreparedRequest [GET]>
(Pdb) req.request.method
'GET'

Как это было перевернуто? Раньше я использовал библиотеку запросов Python без проблем, поэтому я не уверен, что я упускаю что-то очевидное или если (с более новыми версиями Django / Requests) мне нужно установить другой параметр? Это проблема кеширования? Любые советы по отладке? Я попытался переустановить запросы и откатить Django до 1.6.5, но ничего не работает... должно быть просто. -- Спасибо!

====== ОБНОВЛЕНИЕ 1 ========

Просто собрал часть отладочной информации, предложенной Мартийном здесь:

(Pdb) requests.post.__name__
'post'

Шаг в определение request /api.py > post():

(Pdb) l
 88         :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
 89         :param \*\*kwargs: Optional arguments that ``request`` takes.
 90         """
 91         import pdb
 92         pdb.set_trace()
 93  ->     return request('post', url, data=data, **kwargs)

Развертывание в методе request():

(Pdb) method
'post'
(Pdb) l
 43           >>> req = requests.request('GET', 'http://httpbin.org/get')
 44           <Response [200]>
 45         """
 46         import pdb
 47         pdb.set_trace()
 48  ->     session = sessions.Session()
 49         return session.request(method=method, url=url, **kwargs)

Еще один слой, в session.request:

(424)request()
-> method = builtin_str(method)
(Pdb) method
'post'
(Pdb) l
419             :param cert: (optional) if String, path to ssl client cert file (.pem).
420                 If Tuple, ('cert', 'key') pair.
421             """
422             import pdb
423             pdb.set_trace()
424  ->         method = builtin_str(method)
425
426             # Create the Request.
427             req = Request(
428                 method = method.upper(),
429                 url = url,

Если перейти к концу метода, где запрос фактически сделан, моя "prep" - это POST, а моя - GET:

(Pdb) prep
<PreparedRequest [POST]>
(Pdb) n
-> return resp
(Pdb) resp
<Response [200]>
(Pdb) resp.request
<PreparedRequest [GET]>
(Pdb) l
449                 'allow_redirects': allow_redirects,
450             }
451             send_kwargs.update(settings)
452             resp = self.send(prep, **send_kwargs)
453
454  ->         return resp
455
456         def get(self, url, **kwargs):
457             """Sends a GET request. Returns :class:`Response` object.
458
459             :param url: URL for the new :class:`Request` object.

2 ответа

Решение

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

В подобных случаях, когда вы видите что-то очень неожиданное, лучшие советы по отладке - повторить ваш запрос, но с allow_redirects=False, Это немедленно вернет 30-кратный ответ. Кроме того, вы также можете проверить r.history чтобы увидеть, были ли какие-то 30-кратные ответы, которые последовали. В этом случае вы, вероятно, видели бы что-то вроде

>>> r.request.method
'GET'
>>> r.history
[<Response [302]>,]
>>> r.history[0].request.method
'POST'

Мы знаем, что это может привести к неожиданному поведению пользователей (как это было с вами), но это единственный правильный способ работы в Интернете.

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

Спасибо Martijn за некоторые советы по отладке! Проблема заключалась в том, что RESTful API перенаправлял меня с http:// на https://, в результате чего библиотека возвращала "второй" запрос (GET)...

Другие вопросы по тегам