0

I'm in the process of writing a very simple Python application for a friend that will query a service and get some data in return. I can manage the GET requests easily enough, but I'm having trouble with the POST requests. Just to get my feet wet, I've only slightly modified their example JSON data, but when I send it, I get an error. Here's the code (with identifying information changed):

import urllib.request
import json

def WebServiceClass(Object):

    def __init__(self, user, password, host):
        self.user = user
        self.password = password
        self.host = host
        self.url = "https://{}/urldata/".format(self.host)

        mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
        mgr.add_password(None, "https://{}".format(self.host), self.user, self.password)
        self.opener = urllib.request.build_opener(urllib.request.HTTPDigestAuthHandler(mgr))

username = "myusername"
password = "mypassword"
service_host = "thisisthehostinfo"

web_service_object = WebServiceClass(username, password, service_host)

user_query = {"searchFields": 
        {
        "First Name": "firstname",
        "Last Name": "lastname"
        },
        "returnFields":["Entity ID","First Name","Last Name"]
        }

user_query = json.dumps(user_query)
user_query = user_query.encode("ascii")

the_url = web_service_object.url + "modules/Users/search"

try:
    user_data = web_service_object.opener.open(the_url, user_query)
    user_data.read()

except urllib.error.HTTPError as e:
    print(e.code)
    print(e.read())

I got the class data from their API documentation.

As I said, I can do GET requests fine, but this POST request gives me a 500 error with the following text:

Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported

In researching this error, my assumption has become that the above error means I need to submit the data as multipart/form-data. Whether or not that assumption is correct is something I would like to test, but stock Python doesn't appear to have any easy way to create multipart/form-data - there are modules out there, but all of them appear to take a file and convert it to multipart/form-data, whereas I just want to convert this simple JSON data to test.

This leads me to two questions: does it seem as though I'm correct in my assumption that I need multipart/form-data to get this to work correctly? And if so, do I need to put my JSON data into a text file and use one of those modules out there to turn it into multipart, or is there some way to do it without creating a file?

mikev
  • 9
  • 6
  • you can use `io.StringIO` to create file-like object in memory and use it instead of file. But I think module `requests` could be better solution. – furas Feb 01 '17 at 20:45

1 Answers1

2

Maybe you want to try the requests lib, You can pass a files param, then requests will send a multipart/form-data POST instead of an application/x-www-form-urlencoded POST. You are not limited to using actual files in that dictionary, however:

import requests
response = requests.post('http://httpbin.org/post', files=dict(foo='bar'))
print response.status_code

If you want to know more about the requests lib, and specially in sending multipart forms take a look at this:

http://docs.python-requests.org/en/master/ and http://docs.python-requests.org/en/master/user/advanced/?highlight=Multipart-Encoded%20Files

Juan D. Gómez
  • 479
  • 1
  • 7
  • 21
  • Forgive my ignorance - I had started going down this path, but stopped because I couldn't figure out how to use the build_opener method in combination with requests. I'm effectively calling open() on a build_opener object, but I didn't think I could call post() or anything on the build_opener object. – mikev Feb 01 '17 at 21:10
  • If you want to make authenticathed requests, you can invoke requests post method as following: `requests.post('http://httpbin.org/post', files=dict(foo='bar'), auth=HTTPBasicAuth('username', 'password')` so you don't have to use urllib at all. – Juan D. Gómez Feb 01 '17 at 21:15