我今天发现一个奇怪的行为。我通过python请求库在谷歌云消息中发送了一条消息。然后我试着解码json的响应,像这样:
response = requests.post(Message_Broker.host, data=json.dumps(payload), headers=headers)
response_results = json.loads(response.content)["results"]
解码错误导致崩溃:
response_results = json.loads(response.content)["results"]
File "/usr/local/lib/python2.7/dist-packages/simplejson/__init__.py", line 505, in loads
return _default_decoder.decode(s)
File "/usr/local/lib/python2.7/dist-packages/simplejson/decoder.py", line 370, in decode
obj, end = self.raw_decode(s)
File "/usr/local/lib/python2.7/dist-packages/simplejson/decoder.py", line 400, in raw_decode
return self.scan_once(s, idx=_w(s, idx).end())
JSONDecodeError: Expecting value: line 1 column 1 (char 0)
这发生在我的生产系统上,所以我添加了一些调试日志来了解响应的实际内容,如下所示:
logger.info("GCM-Response: " + str(response))
logger.info("GCM-Response: " + response.content)
logger.info("GCM-Response: " + str(response.headers))
现在真正奇怪的行为发生了。它被正确记录并且不再抛出解码错误。
谁能给我解释一下那种行为?我也检查了什么响应。内容实际上是:
@property
def content(self):
"""Content of the response, in bytes."""
if self._content is False:
# Read the contents.
try:
if self._content_consumed:
raise RuntimeError(
'The content for this response was already consumed')
if self.status_code == 0:
self._content = None
else:
self._content = bytes().join(self.iter_content(CONTENT_CHUNK_SIZE)) or bytes()
except AttributeError:
self._content = None
self._content_consumed = True
# don't need to release the connection; that's been handled by urllib3
# since we exhausted the data.
return self._content
是requests
模型的一部分。不是一个实际的属性,但可以通过@property
装饰器访问。据我所知,第一次读取内容进行日志记录时,_content_consumed
标志被设置为True。因此第二次,当我读取json解码时,它实际上应该引发运行时错误。
有一个解释,我只是没有发现浏览请求文档?
因此第二次,当我读取json解码时,它实际上应该引发运行时错误。
不,它不会引起RuntimeError
。当您第一次访问response.content
时,它会将实际数据缓存到self._content
中。在第二次(第三次,第四次等)访问if self._content is False:
是假的,所以你将得到的内容缓存在self._content
。
if self._content_consumed:
检查很可能是内部断言,用于发现从套接字多次读取数据的尝试(这显然是一个错误)。
无法解码为JSON,因为您在响应体中没有收到JSON或收到空体。也许是500或429。