Я пытаюсь использовать python-rest-client (http://code.google.com/p/python-rest-client/wiki/Using_Connection) для тестирования некоторых веб-сервисов RESTful. Поскольку я только учился, я указывал свои тесты на примерах услуг, предоставляемых по адресу http://www.predic8.com/rest-demo.htm.
У меня нет проблем с созданием записей, обновлением записей или извлечением записей (запросы POST и GET). Когда я пытаюсь сделать запрос DELETE, он терпит неудачу. Я могу использовать клиент REST Firefox для выполнения запросов DELETE, и они работают. Я также могу делать запросы DELETE на другие сервисы, но я с ума сходил, пытаясь понять, почему в этом случае это не работает. Я использую Python 3 с обновленным Httplib2, но я также попробовал Python 2.5, чтобы использовать python-rest-client с включенной версией Httplib2. Я вижу ту же проблему в любом случае.
Код прост, соответствующий документированному использованию:
from restful_lib import Connection
self.base_url = "http://www.thomas-bayer.com"
self.conn = Connection(self.base_url)
response = self.conn.request_delete('/sqlrest/CUSTOMER/85')
Я просмотрел полученные HTTP-запросы из инструмента браузера и из моего кода, и я не понимаю, почему он работает, а другой нет. Это след, который я получаю:
Traceback (most recent call last):
File "/home/fmk/python/rest-client/src/TestExampleService.py", line 68, in test_CRUD
self.Delete()
File "/home/fmk/python/rest-client/src/TestExampleService.py", line 55, in Delete
response = self.conn.request_delete('/sqlrest/CUSTOMER/85')
File "/home/fmk/python/rest-client/src/restful_lib.py", line 64, in request_delete
return self.request(resource, "delete", args, headers=headers)
File "/home/fmk/python/rest-client/src/restful_lib.py", line 138, in request
resp, content = self.h.request("%s://%s%s" % (self.scheme, self.host, '/'.join(request_path)), method.upper(), body=body, headers=headers )
File "/home/fmk/python/rest-client/src/httplib2/__init__.py", line 1175, in request
(response, content) = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey)
File "/home/fmk/python/rest-client/src/httplib2/__init__.py", line 931, in _request
(response, content) = self._conn_request(conn, request_uri, method, body, headers)
File "/home/fmk/python/rest-client/src/httplib2/__init__.py", line 897, in _conn_request
response = conn.getresponse()
File "/usr/lib/python3.2/http/client.py", line 1046, in getresponse
response.begin()
File "/usr/lib/python3.2/http/client.py", line 346, in begin
version, status, reason = self._read_status()
File "/usr/lib/python3.2/http/client.py", line 316, in _read_status
raise BadStatusLine(line)
http.client.BadStatusLine: ''
Какое нарушение? Что мне с этим делать? На самом деле, я бы согласился на консультацию по его отладке. Я изменил домен в своем скрипте и указал его на свою собственную машину, чтобы я мог просмотреть запрос. Я просмотрел/изменил запросы Firefox в BurpProxy, чтобы они соответствовали моим запросам скриптов. Измененные запросы Burp по-прежнему работают, и запросы Python по-прежнему не работают.
По-видимому, проблема в том, что сервер ожидает, что для запросов DELETE будет некоторый текст сообщения. Это необычное ожидание для DELETE, но, указав Content-Length: 0 в заголовках, я могу успешно выполнить DELETE.
Где-то по пути (в python-rest-client или httplib2) заголовок Content-Length уничтожается, если я пытаюсь сделать:
from restful_lib import Connection
self.base_url = "http://www.thomas-bayer.com"
self.conn = Connection(self.base_url)
response = self.conn.request_delete('/sqlrest/CUSTOMER/85', headers={'Content-Length':'0'})
Чтобы доказать эту концепцию, я подошел к точке трассировки стека, где происходил запрос:
File "/home/fmk/python/rest-client/src/httplib2/__init__.py", line 897, in _conn_request
response = conn.getresponse()
Я напечатал параметр заголовков там, чтобы подтвердить, что длина содержимого не была там, и я добавил:
if(method == 'DELETE'):
headers['Content-Length'] = '0'
перед запросом.
Я думаю, что реальный ответ заключается в том, что услуга неуклюжая, но, по крайней мере, я узнал httplib2 немного лучше. Я видел, как некоторые другие запутанные люди искали помощь с REST и Python, поэтому, надеюсь, я не единственный, кто получил что-то из этого.
Следующий скрипт правильно производит 404
ответ от сервера:
#!/usr/bin/env python3
import http.client
h = http.client.HTTPConnection('www.thomas-bayer.com', timeout=10)
h.request('DELETE', '/sqlrest/CUSTOMER/85', headers={'Content-Length': 0})
response = h.getresponse()
print(response.status, response.version)
print(response.info())
print(response.read()[:77])
python -V
=> 3.2
curl -X DELETE http://www.thomas-bayer.com/sqlrest/CUSTOMER/85
curl: (52) Empty reply from server
Status-Line не является обязательным; HTTP-сервер должен вернуть его. Или, по крайней мере, отправить 411 Length Required response.
curl -H 'Content-length: 0' -X DELETE \
http://www.thomas-bayer.com/sqlrest/CUSTOMER/85
Правильно возвращается 404
.
curl -H 'Content-length: 0' -X DELETE http://www.thomas-bayer.com/sqlrest/CUSTOMER/85
возвращает непустой ответ.