awesto / django-shop

A Django based shop system

Home Page:http://www.django-shop.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

bug: error message "JSON parse error..." after REST-query to add-to-cart?format=json

SergeyMatsiupa opened this issue · comments

environment:
OS xUbuntu 18.04.4; Python 3.6.9; Django 3.0.8; django-shop 1.2.2 (installed according to instructions https://django-shop.readthedocs.io/en/latest/tutorial/intro.html)
STR:

  1. get cookie-values csrftoken and sessionid adding 1 product to cart in browser and parsing them from headers of corresponding http-request
  2. execute Python-code (adding 1 products unit to existing cart according to instruction 17.2.3.3. Add Product to Cart)
import httplib2
from urllib.parse import urlencode
h = httplib2.Http('.cache')
data = {
  "quantity": 1,
  "product": 15,
  "product_code": "1004"
}
data = urlencode(data)
response, content = h.request('http://localhost:8000/catalog/smart-cards/sdxc-card-64gb/add-to-cart?format=json','POST',data,headers={'Content-Type':'application/json','accept':'application/json','Cookie':'csrftoken=3hoZkpJNer2kSNaDgO3nOa8r2fLNl6KiwtMJDCW5pbDN9jELORM2dDIYCnE8wwew;sessionid=2k0t3edvimyud5acdpvt71e5dda7jod7'})
print("response.status  -  {0}".format(response.status))
print("response.items()  -  {0}".format(response.items()))
print("content  -  {0}".format(content))

AR:
content - b'{"detail":"JSON parse error - Expecting value: line 1 column 1 (char 0)"}'
ER:
understandable error message in the response body

before sending a dict as JSON, you have to stringify it.

...
import json
...
data = {
  "quantity": 1,
  "product": 15,
  "product_code": "1004"
}
data = json.dumps(data)
...

Thanks I done this, but anyway got the same error ("JSON parse error - Expecting value: line 1 column 1 (char 0)", response code 400).
So I made some investigation and find out that error occurs in django_shop_2_slug-UAkksQbw/lib/python3.6/site-packages/rest_framework/parsers.py file in parse function of JSONParser class on the return json.load(decoded_stream, parse_constant=parse_constant) line of code. I checked beforehand that decoded_stream.read() returned exactly that I sent in POST-query -

{"quantity": 1, "product": 15, "product_code": "1004"}

So problem as I think should be in json.load() function (when I tried to execute it there in more simple way as t = json.load(decoded_stream) I also got error).

as far as I know, JSON data must be transferred (and will be received) as byte-stream, not string. Maybe you have to check this as well.

Well, I made some more investigations, and found out for now -
looks like the root cause of the issue is in the stream parameter that is passed to method def parse(self, stream, media_type=None, parser_context=None): of the JSONParser class (declared in the django_shop_2_slug-UAkksQbw/lib/python3.6/site-packages/rest_framework/parsers.py file). This parameter stream is belong to the WSGIRequest class, so according to the docstring of the def method - it should have a 'bytestream nature' and be deserialized by json.load() function well, but it doesn't. I checked it inside the parse method via adding and executing in the first linestream.read() function that returned correct JSON-byte-string - b'{"quantity": 1, "product": 15, "product_code": "1004"}', it's exactly that I passed to the REST-endpoint on the 'frontend-side', and also it should be well enough for json.load() corresponding to it's documentation:

json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
Deserialize fp (a .read()-supporting text file or binary file containing a JSON document) to a Python object using this conversion table.

But executing json.load(stream) in the beginning of the parse method result in an error - 'json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)'
So I checked this case in separated code (and especially for my JSON en- and de-coding):

#JSON for to pass to the REST-endpoint
data = {
  "quantity": 1,
  "product": 15,
  "product_code": "1004"
}
#stringify JSON for sending in request
import json
data = json.dumps(data)
##reproduce server-side JSON-decoding
import codecs, io
#making bytestream object; string is exactly that was passed to JSONParser.parse (checked inside of it)
my_stream = io.BytesIO(b'{"quantity": 1, "product": 15, "product_code": "1004"}')
#it's exactly that should be returned by by the JSONParser.parse and now it's executed successfully
data1 = json.load(my_stream)

@jrief I should ask you for some help and advice - what should I check next? actually, to have well-working REST in my django-shop is very important for now

well, looks as I got rid of this bug just by updating files in the django_shop_2_slug-UAkksQbw/lib/python3.6/site-packages/rest_framework/ project folder to corresponding ones from the official Django REST framework repository