为什么我无法通过pyCurl向Django发送POST请求?

1 投票
1 回答
1105 浏览
提问于 2025-04-18 08:48

我在用pyCurl写脚本连接本地的Django-Tastypie REST网络服务器时遇到了一些奇怪的问题。

当我使用除了pycurl以外的任何工具(包括curl)发送HTTP PUT请求时,服务器都能正常响应,但用pycurl发送时却出现了400错误。

经过大量的搜索和尝试,我现在真的不知道该怎么办。 这到底是怎么回事呢?

有效的Curl调用:

curl --verbose -X PUT -H 'Content-Type: application/json' -d '{"first_name": "Gaius","id": 1,"last_name": "Balthazar","login": "gbalthazar"}' http://localhost:8000/api/person/1/

无效的PyCurl调用(错误400):

import pycurl
import StringIO
curl = pycurl.Curl()
url = 'http://localhost:8000/api/person/1/'
curl.setopt(pycurl.URL,url)
curl.setopt(pycurl.VERBOSE, 1)
body = '{"first_name": "Gaius","id": 1,"last_name": "Baltar","login": "gbaltar"}'
curl.setopt(pycurl.READFUNCTION, StringIO.StringIO(body).read)
curl.setopt(pycurl.UPLOAD, 1)
curl.setopt(pycurl.HTTPHEADER,['Content-Type: application/json','Expect:'])
curl.setopt(curl.TIMEOUT, 5)
curl.perform()

(我也尝试过去掉Expects头部,虽然在pycurl调用中看到头部设置为100-Continue,但结果还是一样。)

不幸的是,这个项目确实需要pycurl对HTTP时间统计的低级访问来测量性能,所以我不能用其他HTTP/REST库来实现。

Curl调用的输出:

* About to connect() to localhost port 8000 (#0)
*   Trying 127.0.0.1...
* connected
* Connected to localhost (127.0.0.1) port 8000 (#0)
> PUT /api/person/1/ HTTP/1.1
> User-Agent: curl/7.27.0
> Host: localhost:8000
> Accept: */*
> Content-Type: application/json
> Content-Length: 78
> 
* upload completely sent off: 78 out of 78 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Thu, 05 Jun 2014 23:45:26 GMT
< Server: WSGIServer/0.1 Python/2.7.3
< Vary: Accept
< X-Frame-Options: SAMEORIGIN
< Content-Type: application/json
< 
* Closing connection #0
{"first_name": "Gaius", "id": 1, "last_name": "Balthazar", "login": "gbalthazar", "pk": "1", "resource_uri": "/api/person/1/"}

PyCurl详细调用的输出:

* About to connect() to localhost port 8000 (#0)
*   Trying 127.0.0.1...
* connected
* Connected to localhost (127.0.0.1) port 8000 (#0)
> PUT /api/person/1/ HTTP/1.1
User-Agent: PycURL/7.27.0
Host: localhost:8000
Accept: */*
Transfer-Encoding: chunked
Content-Type: application/json

* HTTP 1.0, assume close after body
< HTTP/1.0 400 BAD REQUEST
< Date: Thu, 05 Jun 2014 23:44:25 GMT
< Server: WSGIServer/0.1 Python/2.7.3
< X-Frame-Options: SAMEORIGIN
< Content-Type: application/json
< 
* Closing connection #0
{"error": ""}

我这里缺少了什么呢?

1 个回答

2

找到了答案:
需要请求体的长度才能正确处理。

对于POST请求:

curl.setopt(pycurl.POSTFIELDSIZE, len(body))  

对于PUT请求:

curl.setopt(pycurl.INFILESIZE, len(body))

(没错,不同的HTTP请求有不同的选项……这就是libcurl的特点。)

不太确定是什么导致了这种行为,但上面的解决办法有效,现在测试也通过了。

编辑:添加了这个的详细pycurl输出:

* About to connect() to localhost port 8000 (#0)
*   Trying 127.0.0.1...
* connected
* Connected to localhost (127.0.0.1) port 8000 (#0)
> PUT /api/person/1/ HTTP/1.1
User-Agent: PycURL/7.27.0
Host: localhost:8000
Accept: */*
Content-Type: application/json
Content-Length: 72

* We are completely uploaded and fine
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Fri, 06 Jun 2014 17:41:38 GMT
< Server: WSGIServer/0.1 Python/2.7.3
< Vary: Accept
< X-Frame-Options: SAMEORIGIN
< Content-Type: application/json
< 
* Closing connection #0
{"first_name": "Gaius", "id": 1, "last_name": "Baltar", "login": "gbaltar", "pk": "1", "resource_uri": "/api/person/1/"}

撰写回答