2
votes

I'm trying to POST some JSON and a binary file from an iPhone to a Django server running django-piston using ASIHTTPRequest

I know how to get it to work if I am ONLY sending JSON strings, and I know how to make it work if I am ONLY sending a file, but doing both is tricky.

So we'll start with ASIHTTPRequest code

ASIFormDataRequest *request = [[ASIFormDataRequest alloc] initWithURL:url];

[request setRequestMethod:@"POST"];

[request setPostFormat:ASIMultipartFormDataPostFormat];

[request appendPostData:[@"{\"save\":{\"name\":\"iostest\"}}" dataUsingEncoding:NSUTF8StringEncoding]];

[request addData:UIImageJPEGRepresentation([UIImage imageNamed:@"test.jpg"], 1.0f)
    withFileName:@"test.jpg"
  andContentType:@"image/jpeg"
          forKey:@"data"];

[request setDelegate:self];

[request startAsynchronous];

My best idea here is that adding raw string data directly to the POST body and then adding a file just doesn't work.

But if I instead try

[request setPostValue:@"{\"name\":\"iostest\"}" forKey:@"save"];

Then the piston data dictionary will store ['save'] as a string instead of a deserialized object, so it will literally deliver the string

"{\"name\":\"iostest\"}"

Here's my Piston handler code

def create(self, request):

     data = request.data

     print(data['save']) #{\"name\":\"iostest\"}"
     print("Files: " + request.FILES['data'].name) #test.jpg
     print("Data Save Name: " + data['save']['name']) #crash, interprets this as a string indeces lookup

Ideas are welcome.

2

2 Answers

1
votes

I have basically hacked my way around this.

The basic problem is that the request format in which Django expects files to be submitted to the server is one which django-piston literally just drops the ball on.

When it encounters multipart requests, it simply doesn't try to parse the data.

The solution to this problem is to manually call the parsing engine, which, in the case of JSON, is straight out of django.utils (which is kind of disappointing).

You achieve this by using ASIHTTPRequest (or the request module of your choice) to set a standard post value by key, and then access it the old fashioned way.

from django.utils import simplejson
data = simplejson.loads(request.POST['save'])

Which basically just reduces this handler method at this point to nothing more than a regular old Django view in terms of the steps you have to take to get it going.

So clearly, django-piston is not built to deal with files apparently?

1
votes

My best idea here is that adding raw string data directly to the POST body and then adding a file just doesn't work.

That wouldn't work, no. If you're POSTing form data using 'application/x-www-form-urlencoded' format, or 'multipart/form-data' you're not going to be able to just tack some extra data on the end - it needs to go in as part of the form data. Something like this I guess...

[request setPostValue:@"{\"save\":{\"name\":\"iostest\"}}" forKey:@"data"];

But if I remove the string data and only post the file it still doesn't work.

Is more problematic...

or if it's Piston erroneously misreading the data.

I probably wouldn't look in that direction first - piston doesn't really mess with the request object, so it seems more likely that the ASI request isn't quite right.

I think the place to start would be to inspect the incoming request and check that it really is a valid formPOST request:

  1. Check that request["CONTENT_TYPE"] is set to 'multipart/form-data'
  2. Inspect the request.raw_post_data and make sure that it is valid form data as specified in http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 - check that the key names are as you expected and that the file content is present. (Obviously you'll want to use a small text file when you're testing this!)
  3. Check which keys actually are present in request.FILES, if any, in case it's as simple as something like a misnamed field.

Failing all that I'd try to narrow down if it's a problem on the client or server side by trying to write a plain python client and seeing if you have the same issue then. Looking around, something like this: http://atlee.ca/software/poster/ might be useful.