Search code examples
pythonjsonurllibzabbix

zabbix API json request with python urllib.request


I'm working on my python project and I migrated from python2.6 to python 3.6. So I had to replace urllib2 with urllib.request ( and .error and .parse ).

But I'm facing an issue I can't solve, here it is...

I want to send a request written in JSON like below :

import json
import urllib2

data= json.dumps({
        "jsonrpc":"2.0",
        "method":"user.login",
        "params":{
             "user":"guest",
             "password":"password"
        }
        "id":1,
        "auth":None
     })

with urllib2 I faced no issue, I just had to create the request with :

req=urllib2.Request("http://myurl/zabbix/api_jsonrpc.php",data,{'Content-type':'application/json})

send it with:

response=urllib2.urlopen(req)

and it was good but now with urllib.request, I have met many error raised by the library. check what I did ( the request is the same within 'data') :

import json
import urllib.request

data= json.dumps({
        "jsonrpc":"2.0",
        "method":"user.login",
        "params":{
             "user":"guest",
             "password":"password"
        }
        "id":1,
        "auth":None
     })
req = urllib.request.Request("http://myurl/zabbix/api_jsonrpc.php",data,{'Content-type':'application/json})

response = urllib.request.urlopen(req) 

and I get this error :

Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/tmp/Python-3.6.1/Lib/urllib/request.py", line 223, in urlopen
    return opener.open(url, data, timeout)
  File "/tmp/Python-3.6.1/Lib/urllib/request.py", line 524, in open
    req = meth(req)
  File "/tmp/Python-3.6.1/Lib/urllib/request.py", line 1248, in do_request_
raise TypeError(msg)
TypeError: POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type str.

So I inquired about this and learned that I must use the function urllib.parse.urlencode() to convert my request into bytes, so I tried to use it on my request :

import urllib.parse

dataEnc=urllib.parse.urlencode(data)

another error occured :

Traceback (most recent call last):
  File "/tmp/Python-3.6.1/Lib/urllib/parse.py", line 842, in urlencode
    raise TypeError
TypeError

During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/tmp/Python-3.6.1/Lib/urllib/parse.py", line 850, in urlencode
    "or mapping object").with_traceback(tb)
  File "/tmp/Python-3.6.1/Lib/urllib/parse.py", line 842, in urlencode
    raise TypeError
TypeError: not a valid non-string sequence or mapping object

and I realized that json.dumps(data) just convert my array/dictionnary into a string, which is not valid for the urllib.parse.urlencode function, soooooo I retired the json.dumps from data and did this :

import json
import urllib.request
import urllib.parse

data= {
        "jsonrpc":"2.0",
        "method":"user.login",
        "params":{
             "user":"guest",
             "password":"password"
         }
         "id":1,
         "auth":None
      }

dataEnc=urllib.parse.urlencode(data) #this one worked then

req=urllib.request.Request("http://myurl/zabbix/api_jsonrpc.php",data,{'Content-type':'application/json})

response = urllib.request.urlopen(req) #and this one too, but it was too beautiful

then I took a look in the response and got this :

b'{"jsonrpc":"2.0",
   "error":{
         "code":-32700,
         "message":"Parse error",
         "data":"Invalid JSON. An error occurred on the server while parsing the JSON text."}
    ,"id":1}

And I guess it's because the JSON message is not json.dumped !

There is always one element blocking me from doing the request correctly,

so I'm totally stuck with it, if any of you guys have an idea or an alternative I would be so happy.

best Regards

Gozu09


Solution

  • In fact you just need to pass your json data as a byte sequence like this:

    data= {
        "jsonrpc":"2.0",
        "method":"user.login",
        "params":{
            "user":"guest",
            "password":"password"
        }
        "id":1,
        "auth":None
    }
    
    req = urllib.request.Request(
        "http://myurl/zabbix/api_jsonrpc.php",
        data=json.dumps(data).encode(),  # Encode a string to a bytes sequence
        headers={'Content-type':'application/json}
    )
    

    POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type str

    This error means that the data argument is expected to be an iterables of bytes.

    st = "This is a string"
    by = b"This is an iterable of bytes"
    by2 = st.encode() # Convert my string to a bytes sequence
    st2 = by.decode() # Convert my byte sequence into an UTF-8 string
    

    json.dumps() returns a string, therefore you have to call json.dumps().encode() to convert it into a byte array.

    By the way, urlencode is used when you want to convert a string that will be passed as an url argument (i.e: converting spaces characters to "%20"). The output of this method is a string, not a byte array