Box File Upload API using Objective C - iphone

I have been trying out for a while using Box 2.0 API to upload a file from Objective C client to my Box folder. I've read a few posts from:
how to send form data in a programmatic file upload in box api 2.0
API 2.0 how to upload file with POSTMAN?
I've tried successfully using the Curl, as mentioned in the documentation, but always get a 404 when trying to create a NSMutableUrlRequest.
This is my code:
NSURL *URL = [NSURL URLWithString:#"https://api.box.com/2.0/files/content"];
urlRequest = [[NSMutableURLRequest alloc]
initWithURL:URL cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:30];
[urlRequest setHTTPMethod:#"POST"];
AppDelegate *appDelegate = [AppDelegate sharedDelegate];
NSString *p = [NSString stringWithFormat:#"BoxAuth api_key=%#&auth_token=%#",API_KEY,appDelegate.boxAuthToken];
[urlRequest setValue:p forHTTPHeaderField:#"Authorization"];
[urlRequest setValue:#"multipart/form-data, boundary=AaB03x" forHTTPHeaderField:#"Content-Type"];
NSString *postBody = #"--AaB03x"
#"content-disposition: form-data; name=\"filename\"; filename=\"test.txt\";"
#"folder_id=466838434"
#"Content-type: text/plain"
#""
#"testing box api 2.0"
#""
#"--AaB03x--";
NSData *data = [postBody dataUsingEncoding:NSUTF8StringEncoding];
[urlRequest setHTTPBody:data];
[urlRequest setValue:[NSString stringWithFormat:#"%d",[data length]] forHTTPHeaderField:#"Content-Length"];

There are a couple of problems that I see with the way you're constructing the postBody. Having newlines between string literals in your code simply concatenates them. You actually need to have carriage return and line feed to separate different parts of your HTTP body. Also, you mashed both of your form elements in one. The file and folder_id are two separate form elements. You could try something like this:
NSString *postBody = #"\r\n--AaB03x\r\n"
"Content-Disposition: form-data; filename=\"test.txt\"\r\n"
"Content-Type: text/plain\r\n\r\n"
"testing box api 2.0"
"\r\n--AaB03x\r\n"
"Content-Disposition: form-data; name=\"folder_id\";\r\n\r\n"
"0"
"\r\n--AaB03x--\r\n\r\n";
I think that should work provided everything else is set up properly.

Use http://allseeing-i.com/ASIHTTPRequest/
It makes dealing with multipart forms much easier!

Related

NSURLConnection asynchronous request cuts off body during send

I have a method that periodically sends data to a server and receives a response code back. I implemented NSURLConnection's sendAsynchronousRequest for it, and it was working with small data fine. I've had a chance to test sending base64 encoded pictures, which is a common task for the app, and am finding that it cuts off after a few characters of the encoded image. Here's the code involved.
// Initial setup
NSString *urlStr = #"https://example.com/";
urlRequest = [[NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr]] retain];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[urlRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
// The actual sending part
- (void)sendRequest:(NSString *)reqStr {
#try {
if (sendRequestStatus == kRequestState_waiting) {
sendRequestStatus = kRequestState_processing;
NSString *dataStr = [NSString stringWithFormat:#"request_data=%#",reqStr];
NSLog(#"datastr is %#",dataStr);
NSData *dataForUrl = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"nsdata length is: %i",[dataForUrl length]);
[urlRequest setHTTPBody:dataForUrl];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *res, NSData *data, NSError *err) {
// housekeeping
}];
}
}
I get no errors in the logs. The log statement that prints out the dataStr prints the correct data. The log statement that prints the length of the data printed out 183384 bytes, which seems appropriate for the picture. On the server however, the post body is cut off a few characters after the start of the encoded image data. An example is /9j/4 where 4 was the last character the server received.
I'm testing this on an iPhone 3Gs. This is a phonegap application, and testing the same on my Android version has no issues, so the problem must be in my implementation of sending the data. I have not heard any problems from another tester using an iPhone 4 or 4s, so it could be an issue of processing speed. What can I do to tighten this up and prevent the post body from ever cutting off?
I didn't test it, but maybe the problem is this:
[urlRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
I would try with this:
[urlRequest setValue:#"text/plain" forHTTPHeaderField:#"Content-Type"];
because you are sending a base 64 encoded string.

How to send HTTP POST request with gziped content?

I'm developing iPhone app and manually constructing POST requests. Currently, need to compress JSON data before sending it, so looking how to tell a server the content is compressed. Setting content type header to gzip might be not acceptable because server expects JSON data. I'm looking for transparent solution, something like just to add some header telling JSON data is compressed into gzip.
I know, the standard way is to tell the server that the client accepts encoding, but you need to make GET request with accept encoding header first. In my case, I want to post the data already encoded.
Include a Obj-C gzip wrapper, for example NSData+GZip, and use it to encode the body of your NSURLRequest. Also remember to set the Content-Encoding accordingly, so the webserver will know how to treat your request.
NSData *requestBodyData = [yourData gzippedData];
NSString *postLength = [NSString stringWithFormat:#"%d", requestBodyData.length];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"gzip" forHTTPHeaderField:#"Content-Encoding"];
[request setHTTPBody:requestBodyData];
Implmenting some general Method such as follows and setting appropriate Header might help you.
// constructing connection request for url with no local and remote cache data and timeout seconds
NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:[NSURL URLWithString:callingWebAddress]];// cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:timoutseconds];
[request setHTTPMethod:#"POST"];
NSMutableDictionary *headerDictionary = [NSMutableDictionary dictionary];
[headerDictionary setObject:#"application/json, text/javascript" forKey:#"Accept"];
[headerDictionary setObject:#"application/json" forKey:#"Content-Type"];
//Edit as #centurion suggested
[headerDictionary setObject:#"Content-Encoding" forKey:#"gzip"];
[headerDictionary setObject:[NSString stringWithFormat:#"POST /Json/%# HTTP/1.1",method] forKey:#"Request"];
[request setAllHTTPHeaderFields:headerDictionary];
// allocation mem for body data
self.bodyData = [NSMutableData data];
[self appendPostString:[parameter JSONFragment]];
// set post body to request
[request setHTTPBody:bodyData];
NSLog(#"sending data %#",[[[NSString alloc] initWithData:bodyData encoding:NSUTF8StringEncoding]autorelease]);
// create new connection for the request
// schedule this connection to respond to the current run loop with common loop mode.
NSURLConnection *aConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
//[aConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
self.requestConnenction = aConnection;
[aConnection release];

adding + before using %2b using url encoding in iphone

I have an application where on a button click i am passing my server api method which calls the JSON post method and saves data to server database.Here i am saving my mobile number and emergency no to server database.My mobile number is in string format.In my mobile number string variable, my mobile number is getting saved which is in this format '+90-9491491411'.I am entering + and then code and then- and then number but when i send to the server database i am removing the - and sending the no to database but problem is in my server database + of mobile is not getting entered which i am entering .What may be the problem .I am using POST method to send the request .This is my code
-(void)sendRequest
{
NSString *newstring = txtMobile.text;
mobileValue = [newstring stringByReplacingOccurrencesOfString:#"-" withString:#""];
NSLog(#"%#",mobileValue);
NSString *newString1 = txtemergencyprovider.text;
emergencyNumber = [newString1 stringByReplacingOccurrencesOfString:#"-" withString:#""];
NSLog(#"%#",emergencyNumber);
if ([txtEmail.text isEqualToString:#""])
{
post = [NSString stringWithFormat:#"CommandType=new&ApplicationType=%d&FullName=%#&Mobile=%#&EmergencymobileNumber=%#&Latitude=%f&Longitude=%f&City=%#&MobileModel=Apple",applicationtype,txtFullname.text,mobileValue,emergencyNumber,latitude,longitude,txtCity.text];
NSLog(#"%#",post);
}
else {
post = [NSString stringWithFormat:#"CommandType=new&ApplicationType=%d&FullName=%#&Mobile=%#&EmergencymobileNumber=%#&Latitude=%f&Longitude=%f&City=%#&EmailAddress=%#&MobileModel=Apple",applicationtype,txtFullname.text,mobileValue,emergencyNumber,latitude,longitude,txtCity.text,txtEmail.text];
NSLog(#"%#",post);
}
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSLog(#"%#",postLength);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"http://myapi?RequestType=NEW"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (theConnection) {
webData = [[NSMutableData data] retain];
NSLog(#"%#",webData);
}
else
{
}
}
//In my mobile number and emrgency number variable my no is in this format '+91986444711' but when the value is entered in server database + gets removed off .What may be the prob.
Unfortunately, NSString's -stringByAddingPercentEscapesUsingEncoding: will not convert the plus (+) sign into %2B because the plus sign is a valid URL character that is used to separate query parameters. What this usually means is that it gets converted to a space character by the web server.
The easiest way to replace the plus sign would be using NSString's stringByReplacingOccurrencesOfString:withString: to replace the + with %2B. For example:
mobileValue = [mobileValue stringByReplacingOccurrencesOfString:#"+" withString:#"%2B"];
The plus sign ("+") in URL means an encoded space (" ") and most likely your server will interpret it as a space. Change the plus character to %2B in the string before posting it. For a complete solution on URL encoding see this post: http://madebymany.com/blog/url-encoding-an-nsstring-on-ios
The plus sign means space in a post request. You need to convert the plus to a percent escape character. The easiest way to do this is as follows:
NSString* escapedMobileValue = [mobileValue stringByReplacingOccurencesOfString: #"+" withString: #"%2b"];
This will turn the + into %2b. Probably the server will automatically reverse the encoding for you.
(Edited in line with mttrb's comment)

How do I send XML POST data from an iOS app to a Django app?

I am attempting to implement an online leaderboard in a game app for iOS, using Django to process POST requests from the iDevice and store the scores. I have figured out how to get Django to serialize the objects to XML, and my iPhone can read and display the scores. However, I can't for the life of me get my iPhone to POST XML to my Django server.
Below is the function I am using to post the scores...
iOS (Objective-C) Controller:
- (void) submitHighScore {
NSLog(#"Submitting high score...");
NSString *urlString = HIGH_SCORES_URL;
NSURL *url = [NSURL URLWithString: urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url];
[request setHTTPMethod: #"POST"];
[request setValue: #"text/xml" forHTTPHeaderField: #"Content-Type"];
NSMutableData *highScoreData = [NSMutableData data];
[highScoreData appendData: [[NSString stringWithFormat: #"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"] dataUsingEncoding: NSUTF8StringEncoding]];
[highScoreData appendData: [[NSString stringWithFormat: #"<player_name>%#</player_name", #"test"] dataUsingEncoding: NSUTF8StringEncoding]];
[highScoreData appendData: [[NSString stringWithFormat: #"<score>%d</score>", 0] dataUsingEncoding: NSUTF8StringEncoding]];
[highScoreData appendData: [[NSString stringWithFormat: #"</xml>"] dataUsingEncoding: NSUTF8StringEncoding]];
[request setHTTPBody: highScoreData];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: YES];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest: request
delegate: self];
if (!connection) {
NSLog(#"Request to send high scores appears to be invalid.");
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: NO];
}
}
The above method succeeds in sending the request, and interprets it correctly as CONTENT_TYPE: text/xml, but the Django view that processes the request can't seem to make any sense of it, interpreting it almost as if it was merely plain text. Below is my Django view...
Django (Python) view:
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.core import serializers
from django.core.exceptions import ValidationError
from django.views.decorators.csrf import csrf_exempt
from modologger.taptap.models import HighScore
#csrf_exempt
def leaderboard( request, xml = False, template_name = 'apps/taptap/leaderboard.html' ):
"""Returns leaderboard."""
if xml == True: # xml is set as True or False in the URLConf, based on the URL requested
if request.method == 'POST':
postdata = request.POST.copy()
print postdata
# here, postdata is evaluated as:
# <QueryDict: {u'<?xml version': [u'"1.0" encoding="UTF-8" ?><player_name>test</player_name<score>0</score></xml>']}>
for deserialized_object in serializers.deserialize('xml', postdata): # this fails, returning a 500 error
try:
deserialized_object.object.full_clean()
except ValidationError, e:
return HttpResponseBadRequest
deserialized_object.save()
else:
high_score_data = serializers.serialize( 'xml', HighScore.objects.all() )
return HttpResponse( high_score_data, mimetype = 'text/xml' )
else:
high_scores = HighScore.objects.all()
return render_to_response( template_name, locals(), context_instance = RequestContext( request ) )
To be honest, I'm not sure whether the problem lies in the Objective-C or in the Django code. Is the Objective-C not sending the XML in the right format? Or is the Django server not processing that XML correctly?
Any insight would be much appreciated. Thanks in advance.
Update:
I got it to work, by editing the iOS Controller to set the HTTPBody of the request like so:
NSMutableData *highScoreData = [NSMutableData data];
[highScoreData appendData: [[NSString stringWithFormat: #"player_name=%#;", #"test"] dataUsingEncoding: NSUTF8StringEncoding]];
[highScoreData appendData: [[NSString stringWithFormat: #"score=%d", 0] dataUsingEncoding: NSUTF8StringEncoding]];
[request setHTTPBody: highScoreData];
For some reason putting a semicolon in there got Django to recognize it, assign the values to a new instance of a HighScore class, and save it. The logging on the test server indicates request.POST is <QueryDict: {u'score': [u'9'], u'player_name': [u'test']}>.
Still not quite sure what to make of all this.
As per Radu's suggestion, I took a look at highScoreData with NSLog, right after appending it to request.HTTPBody, and the result is <706c6179 65725f6e 616d653d 74657374 3b73636f 72653d39>.
I'm a huge Obj-C noob, so again, any help is appreciated! Thanks again.
Since you control both sides, I'd drop the complexity of xml encoding the data and use RestKit or some other framework that makes it easy to communicate with Django.

How to send a transition receipt along with a product identifier to my server?

how to send a transition receipt along with a product identifier to my server using HTTP POST method? Should I have to encode the product identifier using base64?
I am getting this response:
{"message" : "21002: java.lang.IllegalArgumentException: propertyListFromString parsed an object, but there's still more text in the string. A plist should contain only one top-level object. Line number: 1, column: 2478.", "error" : 1}
even after I am encoding the transition receipt with base64 encoding...
This is what I am doing:
NSString *bodyString = [NSString stringWithFormat:#"product_id=%#&receipt_data=%#",self.currentProductIdentifer,
[self.productReceiptData base64Encoding]];
NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding
allowLossyConversion:YES];
NSAssert(bodyData!=nil,#"HTTP Body is empty !");
//posting the data to server
NSMutableURLRequest *theRequest=[NSMutableURLRequest requestWithURL:
[NSURL URLWithString:inUrlString]
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:inData]; //inData is http body(bodyData) created above
// create the connection with the request // and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];