I am trying to use urllib3 in Python to POST x-www-form-urlencoded data to ServiceNow API. The usual curl command would look like this
curl -d "grant_type=password&client_id=<client_ID>&client_secret=<client_Secret>&username=<username>&password=<password>" https://host.service-now.com/oauth_token.do
So far, I have tried the following:
import urllib3
import urllib.parse
http = urllib3.PoolManager()
data = {"grant_type": "password", "client_id": "<client_ID>", "client_secret": "<client_Secret>", "username": "<username>", "password": "<password>"}
data = urllib.parse.urlencode(data)
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
accesTokenCreate = http.request('POST', "https://host.service-now.com/outh_token.do", headers = headers, fields= data)
print(accesTokenCreate.data)
However, it does not generate the result similar to curl command and gives errors like below:
Traceback (most recent call last):
File "/VisualStudio/Python/ServiceNow.py", line 18, in <module>
accesTokenCreate = http.request('POST', "https://visierdev.service-now.com/outh_token.do", headers = headers, fields= data)
File "/usr/local/homebrew/lib/python3.7/site-packages/urllib3/request.py", line 80, in request
method, url, fields=fields, headers=headers, **urlopen_kw
File "/usr/local/homebrew/lib/python3.7/site-packages/urllib3/request.py", line 157, in request_encode_body
fields, boundary=multipart_boundary
File "/usr/local/homebrew/lib/python3.7/site-packages/urllib3/filepost.py", line 78, in encode_multipart_formdata
for field in iter_field_objects(fields):
File "/usr/local/homebrew/lib/python3.7/site-packages/urllib3/filepost.py", line 42, in iter_field_objects
yield RequestField.from_tuples(*field)
TypeError: from_tuples() missing 1 required positional argument: 'value'
Could someone please help me understand how to properly use urllib3 to post such data to the ServiceNow API?
According to the urlllib3 documentation, you are not using the request() method properly. Specifically, the fields parameter in your code is not a "parameter of key/value strings AND key/filetuple". It's not suppose to be a URL-encoded string.
To fix your code, simply change the request call's fields parameter to body as in:
accesTokenCreate = http.request(
'POST', "https://host.service-now.com/outh_token.do",
headers=headers, body=data)
Better yet, you can use the request_encode_body() function and pass in the fields directly without urlencode-ing it and let that function call urllib.parse.urlencode() for you (per the same documentation).
Related
import requests
import json
url = 'mywebsite/test.php'
myobj = data = {"username" : "test", "password" : "1234"}
myobj = json.dumps(myobj)
x = requests.post("loginUser",url, data = myobj)
print(x)
I get the following error:
Traceback (most recent call last):
File "main.py", line 55, in <module> x = requests.post("loginUser",url, data = myobj) TypeError: post() got multiple values for argument 'data'
Can anyone help with this?
Look at the docs:
https://docs.python-requests.org/en/latest/api/
requests.post(url, data=None, json=None, **kwargs)[source]
Sends a POST request.
Parameters:
url – URL for the new Request object.
data – (optional) Dictionary, list of tuples, bytes, or file-like object to send in the body of the Request.
json – (optional) json data to send in the body of the Request.
**kwargs – Optional arguments that request takes.
Returns:
Response object
and so your command should be:
myobj = json.dumps(myobj).encode("ascii")
x = requests.post(url = url, data = myobj)
or without using json.dumps:
x = requests.post(url = url, json = myobj)
What exactly is "loginUser" for in this case? is that a URI route, field, or parameter?
I'm just learning python myself and have used requests a bit.
I've always put the url at the beggining and payload at the end:
i.e Should "LoginUser" go at the end?
I have fixed it. Problem was that I didn’t put http:// in front of the url and I didn’t format my JSON request the way my php was expecting.
I have this cURL:
curl -X POST http://user:pass#blabla.com:8080/job/myproject/config.xml --data-binary "#new_config.xml"
I am basically trying to set a new config for a Jenkins installation by changing the pre-existing config.xml file.
I am trying to convert it to something like this in order to use it more flexibly in my code:
url = "http://host:8080/job/myproject/config.xml"
auth = ('user','pass')
payload = {"--data-binary": "#new_config.xml"}
headers = {"Content-Type" : "application/xml"}
r = requests.post(url, auth=auth, data=payload, headers=headers)
I know that I am using incorrectly the payload and the headers.How should I change them?
I run it and I take a 500 responce code.
I read this post , but I am struggling to apply it in my case.
The --data-binary switch means: post the command line argument as the whole POST body, without wrapping in multipart/form-data or application/x-www-form-encoding containers. # tells curl to load the data from a filename; new_config.xml in this case.
You'll need to open the file object to send the contents as the data argument:
url = "http://host:8080/job/myproject/config.xml"
auth = ('user','pass')
headers = {"Content-Type" : "application/xml"}
with open('new_config.xml', 'rb') as payload:
r = requests.post(url, auth=auth, data=payload, headers=headers)
Note that I pass the file object directly into requests; the data will then be read and pushed to the HTTP socket, streaming the data efficiently.
Apologies if this seems rudimental as I am new to Python. The task I am trying to complete is to send a json object from an iPhone app to a python script that will process a stripe payment. The problem I have is I cannot figure out how to get Python to recognise the incoming json object to extract data from it and pass onto Stripe.
I have taken a step back to simplify the problem. I have a python script that attempts to post a json object with four value pairs to another function that should extract the values, create a new json object and return that object. I cannot get it to work and any help would be greatly appreciated as I've been stuck on this for a while. I am using Flask:
`
import json
import stripe
import smtplib
import requests
from flask import Flask, request, jsonify
#application.route('/run_post')
def run_post():
url = 'http://xxx.pythonanywhere.com/stripetest'
data = {'stripeAmount': '199', 'stripeCurrency': 'USD', 'stripeToken': '122', 'stripeDescription': 'Test post'}
headers = {'Content-Type' : 'application/json'}
r = requests.post(url, data, headers=headers)
#return json.dumps(r.json(), indent=4)
return r.text
#application.route('/stripetest', methods=["POST"])
def stripeTest():
if request.method == "POST":
json_dict = json.loads(request.body.raw)
stripeAmount = json_dict['stripeAmount']
stripeCurrency = json_dict['stripeCurrency']
stripeToken = json_dict['stripeToken']
stripeDescription = json_dict['stripeDescription']
data = "{'stripeAmountRet': " + stripeAmount + ", 'stripeCurrencyRet': " + stripeCurrency + ", 'stripeTokenRet': " + stripeToken + ", 'stripeDescriptionRet': " + stripeDescription + "}"
return jsonify(data)
else:
return """<html><body>
Something went horribly wrong
</body></html>"""
`
I get the following returned in the error log when I run this:
`
2015-03-19 21:07:47,148 :Starting new HTTP connection (1): xxx.pythonanywhere.com
2015-03-19 21:07:47,151 :Exception on /stripetest [POST]
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1687, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1360, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1358, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1344, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/var/www/xxx_pythonanywhere_com_wsgi.py", line 156, in stripeTest
json_dict = json.loads(request.body.raw)
File "/usr/local/lib/python2.7/dist-packages/werkzeug/local.py", line 336, in __getattr__
return getattr(self._get_current_object(), name)
AttributeError: 'Request' object has no attribute 'body'
`
You have a couple of issues with the code. First of all, you need to properly define the json data when you make the request from the requests library. You can do this as follows:
#application.route('/run_post')
def run_post():
url = 'http://xxx.pythonanywhere.com/stripetest'
data = {'stripeAmount': '199', 'stripeCurrency': 'USD', 'stripeToken': '122', 'stripeDescription': 'Test post'}
headers = {'Content-Type' : 'application/json'}
r = requests.post(url, data=json.dumps(data), headers=headers)
#return json.dumps(r.json(), indent=4)
return r.text
Notice that we call json.dumps instead of just passing the data directly. Otherwise, the incoming request is not interpreted as json data.
Next, in your receiving function, we change it as follows:
#application.route('/stripetest', methods=["POST"])
def stripeTest():
if request.method == "POST":
json_dict = request.get_json()
stripeAmount = json_dict['stripeAmount']
stripeCurrency = json_dict['stripeCurrency']
stripeToken = json_dict['stripeToken']
stripeDescription = json_dict['stripeDescription']
data = {'stripeAmountRet': stripeAmount, 'stripeCurrencyRet': stripeCurrency, 'stripeTokenRet': stripeToken, 'stripeDescriptionRet': stripeDescription}
return jsonify(data)
else:
return """<html><body>
Something went horribly wrong
</body></html>"""
A couple of things are changed. First, we read in the data by calling request.get_json(), which properly parses the incoming json data. Note from above that we needed to change how we actually made the request for it to parse the data properly. The next issue was how you returned the data. To properly jsonify the data to return, we put the data into a python dictionary rather than into a string.
If you're calling your function to process the stripe payment from somewhere else (i.e. not using the python requests library), another issue is that you may not be defining the json request properly for Flask to later interpret. If the issue still persists after making the above change to the processing function, post how you're making the json request elsewhere and I can take a look.
Let me know if this fixes your problems!
You should check the document Flask requests
It does not define a body, instead you should try with
request.get_json()
You just need to make sure you are specifying the correct mimetype which would be "application/json".
See request.get_json() method for more info
As an introduction to APIs, I'm trying to figure out how to access data in python using the Rotten Tomatoes API. This is also my first time dealing with json.
I'm using Python 3.4 and have confirmed that json and urllib3 have been installed.
Here is my code:
import urllib3
import json
url = 'http://api.rottentomatoes.com/api/public/v1.0/lists/movies/box_office.json?limit=16&country=us&apikey=API-KEY';
http = urllib3.PoolManager()
request = http.request('GET', url)
print (json.load(request));
request.release_conn()
Here is the error I get:
Traceback (most recent call last):
File "C:\Users\admnasst1\Documents\Personal\Python\RotTomTest.py", line 16, in <module>
print (str(json.load(request)));
File "C:\Python34\lib\json\__init__.py", line 268, in load
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
File "C:\Python34\lib\json\__init__.py", line 312, in loads
s.__class__.__name__))
TypeError: the JSON object must be str, not 'bytes'
Since I'm trying out so many new things (API, urllib3, json), I'm not exactly sure what's going on. I've tried doing a few other versions of the above code, and I keep getting the same error, so i think I must be missing something basic... Can any of you spot it?
You'll need to decode the network data to a a string:
json.loads(request.data.decode('utf8'))
Ideally, you'd detect what codec was used from the Content-Type header; you'd parse that and look for a charset= parameter. The default encoding for JSON data is UTF-8.
If you are going to use a 3rd party library, use requests instead. It wraps urllib3 in a friendly API and can handle JSON for you:
import requests
url = 'http://api.rottentomatoes.com/api/public/v1.0/lists/movies/box_office.json'
params = {'limit': 16, 'country': 'us', 'apikey': 'API-KEY'}
response = requests.get(url, params=params)
print(response.json())
I'm building a REST API with Django and in some places I need to send HTTP GET with many parameters. Because of that I've decided to send them as JSON in the request.body. Now, the app works fine but not the unit testing. I cannot seem to find a way to send the JSON parameters in the body using the self.client.get(). This is what I'm doing:
import json
from django.test import TestCase
from django.core.urlresolvers import reverse
class RestApiTests(TestCase):
def test_analysis():
extra = {'CONTENT_TYPE': 'application/json'}
resp = self.client.get(reverse('restapi_analysis'), data="{'analysisID': 41}", **extra)
self.assertEqual(json.loads(resp.content)['statusText'], 'Analysis fetched successfully')
Using this and running the unittests I get the following error:
Traceback (most recent call last):
File "/home/anascu/svn/django-tc-reporting/tcsite/tcapp/tests/test_restapi.py", line 151, in test_analysis
resp = self.client.get(reverse('restapi_analysis'), data="{'analysisID': 41}", **extra)
File "/home/anascu/virtenv/tc-tracker/local/lib/python2.7/site-packages/django/test/client.py", line 439, in get
response = super(Client, self).get(path, data=data, **extra)
File "/home/anascu/virtenv/tc-tracker/local/lib/python2.7/site-packages/django/test/client.py", line 240, in get
'QUERY_STRING': urlencode(data, doseq=True) or parsed[4],
File "/home/anascu/virtenv/tc-tracker/local/lib/python2.7/site-packages/django/utils/http.py", line 75, in urlencode
for k, v in query],
ValueError: need more than 1 value to unpack
POST works fine but I need to use GET in this case. Is it even possible? Django version is 1.4.5.
Get method does not create request body, you need to use post, put or patch.
I hoped someone knows a hidden feature. Anyway, the workaround I find is the Python Requests module, though in this case one should use the LiveServerTestCase class instead TestCase in Django unit testing. Requests allows sending JSON content in the body.