Python POST with urlencoded multidimensional dictionary - python

I have many functions that successfully POST a urlencoded body using python. However, I have a body that is a multidimensional dictionary. Using this dictionary, I only get a 400 (bad request). bodytmp (below) is an example of the body that works using Fiddler. (The actual url and body can not be supplied.) I have also included a recursive urlencode function that I found here and am using now but with no success.
Does anyone have experience with this type of POST request with a multidimensional dictionary for the urlencoded body?
Thank you. ( I have abbreviated the code to make it more readable, but this is the gist. )
from httplib2 import Http
import httplib
import urllib
def postAgentRegister():
h = Http(disable_ssl_certificate_validation=True)
headers={'Content-Type':'application/x-www-form-urlencoded'}
bodytmp={"Username": "username",
"Password": "password",
"AccountId": "accountid",
"PublicKey": {
"Value1a": "32ab45cd",
"value1b": "10001"
},
"snId": "SN1",
"IpAddresses": {
"Value2a": ["50.156.54.45"],
"Value2b": "null"
}
}
body=recursive_urlencode(bodytmp)
try:
response,content=h.request('https://server/endpoint','POST',headers=headers,body=body)
conn = httplib.HTTPConnection(testserver)
conn.request('POST', endpoint, body, headers)
r1 = conn.getresponse()
status = r1.status
reason = r1.reason
except httplib.HTTPException, e:
status= e.code
print 'status is: ', status
def recursive_urlencode(d):
def recursion(d, base=None):
pairs = []
for key, value in d.items():
if hasattr(value, 'values'):
pairs += recursion(value, key)
else:
new_pair = None
if base:
new_pair = "%s[%s]=%s" % (base, urllib.quote(unicode(key)), urllib.quote(unicode(value)))
else:
new_pair = "%s=%s" % (urllib.quote(unicode(key)), urllib.quote(unicode(value)))
pairs.append(new_pair)
return pairs
return '&'.join(recursion(d))

Might I suggest that you serialize your body to JSON and de-serialize when your server receives it? This way you just have to do a url string encode rather than using your own recursive url encode.

Related

Cannot send JSONs from an external app but my Django test works

Tl;dr: Im trying to send a JSON over http with python, is my first script alright?
Im trying to build a django app where I would send information to add to the database by POSTing json strings from another python application.
To do that, I run this code to send the json (trimmed to the important part)
out_data = {
'session_id': session_id,
'count': count,
'gas_reading': gas_received,
'latitude': latitude,
'longitude': longitude,
'date_time': date_time.strftime("%Y-%m-%d %X")
}
out_json = json.dumps(out_data)
url = 'http://localhost:8000/sessions/post/'
response = requests.post(url, headers={'Content-type':'application/json'}, json=out_json)
print("Posted to ",url," and got ",response)
And this is the definition of the view that catches it.
def post(request):
if request.method=='POST':
received_data=json.loads(request.body)
session_id = int(received_data['session_id'])
if Session.objects.filter(session_id=session_id):
session = Session.objects.get(session_id=session_id)
else:
session = Session(session_id=session_id)
session.save()
session.measurement_set.create(
gas_reading=int(received_data['gas_reading']),
count=int(received_data['count']),
latitude=float(received_data['latitude']),
longitude=float(received_data['longitude']),
date_time = parse_datetime(received_data['date_time'])
)
return HttpResponseRedirect(reverse('index'))
elif request.method=='GET':
return HttpResponse("This is only for posting data")
I tried the View by using this test, which works:
class PostViewTests(TestCase):
def test_post_into_database(self):
data = {
'session_id': 69,
'count':100,
'gas_reading': 420,
'latitude': 4.13,
'longitude': 80.08,
'date_time': '2020-07-13 20:30:00'
}
headers = {'content-type':'application/json'}
self.client.post(reverse('readings:post'), content_type='application/json',data=data)
session=Session.objects.get(session_id=69)
measurement=session.measurement_set.last()
local_date_time = timezone.localtime(measurement.date_time)
self.assertEqual(session.session_id, 69)
self.assertEqual(measurement.count, 100)
self.assertEqual(measurement.gas_reading,420)
self.assertEqual(measurement.latitude,4.13)
self.assertEqual(measurement.longitude,80.08)
self.assertEqual(local_date_time.day,13)
self.assertEqual(local_date_time.month,7)
self.assertEqual(local_date_time.year,2020)
self.assertEqual(local_date_time.hour,20)
self.assertEqual(local_date_time.minute,30)
self.assertEqual(local_date_time.second,00)
I get a TypeError: string indices must be integers and fooling around with the debugger, I see that, indeed, I get a string with the json data instead of a dictionary object. Am I missing something in sending the json?
What is wrong: You're converting your out_data to JSON and still using json argument to pass out_json.
What you should be doing is directly using json argument with dictionary or using data argument with converted json.
response = requests.post(
url, headers={"Content-type": "application/json"}, json=out_data
)
OR
response = requests.post(
url, headers={"Content-type": "application/json"}, data=out_json
)

How to prevent anonymous keys on Firebase?

I'm working on a chat project on the platform Raspberry PI 3, Openelec OS.
Trying to write to the DB and getting unwanted anonymous keys.
Unwanted key marked with yellow. Movie2 and it's keys and values are the wanted result, but I made it manually.
I only ask how can I prevent this anonymous random key to be there and how can I replace it with other key? (string, a movie name for example)
This is my code:
url = 'https://chat-example-97c62.firebaseio.com/Movie1.json'
postdata = {
'date': str(time.asctime( time.localtime(time.time()) )),
'temp': str("Hello from Kodi")
}
req = urllib2.Request(url)
req.add_header('Content-Type','application/json')
data = json.dumps(postdata)
Thanks.
When you send a POST request to Firebase, it automatically generates a Key (Anonymous Key), if you want to use your own key you need to use a PATCH request, this is an example on Python 3:
def update_entry(user, message):
my_data = dict()
my_data["user"] = user
my_data["message"] = message
json_data = json.dumps(my_data).encode()
request = urllib.requests.Request("https://<YOUR-PROJECT-ID>.firebaseio.com/movies/<REPLACE_THIS_WITH_YOUR_DESIRED_KEY>.json", data=json_data, method="PATCH")
try:
loader = urllib.request.urlopen(request)
except urllib.error.URLError as e:
message = json.loads(e.read())
print(message["error"])
else:
print(loader.read())

python function for HTTP calls

I have a script that uses a dictionary stored in "my_dict" variable, and values of url, user, password, along with the "id" variable. The script then does an HTTP GET call to the url depending on headers passed. How do I create a Python Function which is equivalent to this? And how to the function later for another set of url, user, password etc?
import urllib, urllib2, base64, json
my_dict = {'server': {'user': 'patrick', 'url': 'http://192.168.0.1/tasks', 'password': 'secret'}}
id = "8d4lkf8kjhla8EnsdAjkjFjkdb6lklne"
for value in my_dict.keys():
url = my_dict[value]['url']
pass = my_dict[value]['password']
authKey = base64.b64encode("patrick:"+str(pass))
headers = {"login-session": id, "Content-Type": "application/json", "Authorization": "Basic " + authKey}
data = {"param": "value"}
request = urllib2.Request(url)
for key, value in headers.items():
request.add_header(key, value)
response = json.load(urllib2.urlopen(request))
print response
Your question has a lot of subquestions such as what do you want variable or constant? Do you know the syntax? How your function will be used? etc etc.
Therefore the best way for you to get an answer is learn some basics, like here (free) for the functions syntax in python. After that you may very well have the answers to your question.
A simple function like this will work if I understood you correctly,
def send_request(my_dict, id):
for value in my_dict.keys():
url = my_dict[value]['url']
pass = my_dict[value]['password']
authKey = base64.b64encode("patrick:"+str(pass))
headers = {"login-session": id, "Content-Type": "application/json", "Authorization": "Basic " + authKey}
data = {"param": "value"}
request = urllib2.Request(url)
for key, value in headers.items():
request.add_header(key, value)
response = json.load(urllib2.urlopen(request))
print response

Python: issue in displaying as a list httplib.HTTPMessage

I have a small problem here. So, I am writing some calls for a well known REST API. Everything is going well, except the fact that I want all the response to be displayed as a list(which is better for me to manipulate). My function is this:
import sys, httplib
HOST = "api.sugarsync.com"
API_URL = "https://api.sugarsync.com"
def do_request(xml_location):
request = open(xml_location,"r").read()
webservice = httplib.HTTPS(HOST)
webservice.putrequest("POST", "authorization", API_URL)
webservice.putheader("Host", HOST)
webservice.putheader("User-Agent","Python post")
webservice.putheader("Content-type", "application/xml")
webservice.putheader("Content-type", "application/xml")
webservice.putheader("Accept", "*/*")
webservice.putheader("Content-length", "%d" % len(request))
webservice.endheaders()
webservice.send(request)
statuscode, statusmessage, header = webservice.getreply()
result = webservice.getfile().read()
return statuscode, statusmessage, header
return result
do_request('C://Users/my_user/Documents/auth.xml')
I am used to use split() but in this case the result is this:
[201, 'Created', <httplib.HTTPMessage instance at 0x0000000001F68AC8>]
Well, I need also the third object(httplib.HTTPMessage instance at 0x0000000001F68AC8>), to be displayed as list, to extract some of the data in there.
Thanks in advance!
httplib.HTTPMessage is something like dict, here is a sample:
import httplib
from cStringIO import StringIO
h = httplib.HTTPMessage(StringIO(""))
h["Content-Type"] = "text/plain"
h["Content-Length"] = "1234"
print h.items()
you just call it's function items(), it will return a list of headers

Authentication Required - Problems Establishing AIM OSCAR Session using Python

I'm writing a simple python script that will interface with the AIM servers using the OSCAR protocol. It includes a somewhat complex handshake protocol. You essentially have to send a GET request to a specific URL, receive XML or JSON encoded reply, extract a special session token and secret key, then generate a response using the token and the key.
I tried to follow these steps to a tee, but the process fails in the last one. Here is my code:
class simpleOSCAR:
def __init__(self, username, password):
self.username = username
self.password = password
self.open_aim_key = 'whatever'
self.client_name = 'blah blah blah'
self.client_version = 'yadda yadda yadda'
def authenticate(self):
# STEP 1
url = 'https://api.screenname.aol.com/auth/clientLogin?f=json'
data = urllib.urlencode( [
('k', self.open_aim_key),
('s', self.username),
('pwd', self.password),
('clientVersion', self.client_version),
('clientName', self.client_name)]
)
response = urllib2.urlopen(url, data)
json_response = simplejson.loads(urllib.unquote(response.read()))
session_secret = json_response['response']['data']['sessionSecret']
host_time = json_response['response']['data']['hostTime']
self.token = json_response['response']['data']['token']['a']
# STEP 2
self.session_key = base64.b64encode(hmac.new(self.password, session_secret, sha256).digest())
#STEP 3
uri = "http://api.oscar.aol.com/aim/startOSCARSession?"
data = urllib.urlencode([
('a', self.token),
('clientName', self.client_name),
('clientVersion', self.client_version),
('f', 'json'),
('k', self.open_aim_key),
('ts', host_time),
]
)
urldata = uri+data
hashdata = "GET&" + urllib.quote("http://api.oscar.aol.com/aim/startOSCARSession?") + data
digest = base64.b64encode(hmac.new(self.session_key, hashdata, sha256).digest())
urldata = urldata + "&sig_sha256=" + digest
print urldata + "\n"
response = urllib2.urlopen(urldata)
json_response = urllib.unquote(response.read())
print json_response
if __name__ == '__main__':
so = simpleOSCAR("aimscreenname", "somepassword")
so.authenticate()
I get the following response from the server:
{ "response" : {
"statusCode":401,
"statusText":"Authentication Required. statusDetailCode 1014",
"statusDetailCode":1014,
"data":{
"ts":1235878395
}
}
}
I tried troubleshooting it in various ways, but the URL's I generate look the same as the ones shown in the signon flow example. And yet, it fails.
Any idea what I'm doing wrong here? Am I hashing the values wrong? Am I encoding something improperly? Is my session timing out?
Try using Twisted's OSCAR support instead of writing your own? It hasn't seen a lot of maintenance, but I believe it works.
URI Encode your digest?
-moxford

Categories

Resources