84

How do you append data to an existing POST NSURLRequest? I need to add a new parameter userId=2323.

Rob
  • 371,891
  • 67
  • 713
  • 902
Tim
  • 947
  • 1
  • 9
  • 8

6 Answers6

194

If you don't wish to use 3rd party classes then the following is how you set the post body...

NSURL *aUrl = [NSURL URLWithString:@"http://www.apple.com/"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl
                                         cachePolicy:NSURLRequestUseProtocolCachePolicy
                                     timeoutInterval:60.0];

[request setHTTPMethod:@"POST"];
NSString *postString = @"company=Locassa&quality=AWESOME!";
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];

NSURLConnection *connection= [[NSURLConnection alloc] initWithRequest:request 
                                                             delegate:self];

Simply append your key/value pair to the post string

Simon Lee
  • 22,224
  • 4
  • 38
  • 44
  • 2
    I need to add the new parameter to an existing NSURLRequest, not create a new request. I believe it has to be converted to an NSMutableURLRequest first. What I don't know is how to get the existing POST data. – Tim May 27 '11 at 07:49
  • 1
    Is other words how do you get the postString of an existing request? – Tim May 27 '11 at 08:02
  • 1
    You can access the - (NSData *)HTTPBody which you can encode into an NSString, key/value pairs – Simon Lee May 27 '11 at 08:05
  • The existing post data is encoded in UTF8 as in your example. How can I reverse the encoding? Then create a new string with the new parameters? then encode the new post data – Tim May 27 '11 at 08:10
  • 1
    This *should* work but I can't test it at the moment.... NSMutableString *existingPost = [[NSMutableString alloc] initWithData:[req HTTPBody] encoding:NSUTF8StringEncoding]; [existingPost appendFormat:@"&%@=%@", @"name", @"Locassa"]; – Simon Lee May 27 '11 at 10:44
  • It seems that I have to the the NSURLConnection *connection line below the [request setHTTPBody]. I was just hacking around and trying to figure things out so it may not be correct. – okysabeni May 18 '14 at 21:32
  • Where is 'connection' being used? – Arbitur Nov 19 '14 at 08:21
  • @Arbitur It's not used in this case. You might save it for future reference in case you need to cancel the request (e.g. if the user dismisses view but the request is not done). But the `initWithRequest` starts the request and returns the `NSURLConnection` reference in case you need it (and you can check to see that it's non-`nil`, to make sure the request started OK). – Rob Jan 07 '15 at 13:59
  • This answer is not correct, though. You should not start the connection and then proceed to continue modifying the request. (See Kevin's answer below.) You must finish configuring the request first, and only then start the connection. And, obviously, it assumes you've implemented the `NSURLConnectionDataDelegate` methods. And, of course, you should percent escape the values you insert into your `HTTPBody`. – Rob Jan 07 '15 at 14:01
16

All the changes to the NSMutableURLRequest must be made before calling NSURLConnection.

I see this problem as I copy and paste the code above and run TCPMon and see the request is GET instead of the expected POST.

NSURL *aUrl = [NSURL URLWithString:@"http://www.apple.com/"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl
                                     cachePolicy:NSURLRequestUseProtocolCachePolicy
                                 timeoutInterval:60.0];


[request setHTTPMethod:@"POST"];
NSString *postString = @"company=Locassa&quality=AWESOME!";
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];

NSURLConnection *connection= [[NSURLConnection alloc] initWithRequest:request 
                                                         delegate:self];
Praveen Vinny
  • 2,318
  • 6
  • 30
  • 39
Kevin
  • 808
  • 11
  • 13
13

The previous posts about forming POST requests are largely correct (add the parameters to the body, not the URL). But if there is any chance of the input data containing any reserved characters (e.g. spaces, ampersand, plus sign), then you will want to handle these reserved characters. Namely, you should percent-escape the input.

//create body of the request

NSString *userid = ...
NSString *encodedUserid = [self percentEscapeString:userid];
NSString *postString    = [NSString stringWithFormat:@"userid=%@", encodedUserid];
NSData   *postBody      = [postString dataUsingEncoding:NSUTF8StringEncoding];

//initialize a request from url

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPBody:postBody];
[request setHTTPMethod:@"POST"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];

//initialize a connection from request, any way you want to, e.g.

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

Where the precentEscapeString method is defined as follows:

- (NSString *)percentEscapeString:(NSString *)string
{
    NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                                 (CFStringRef)string,
                                                                                 (CFStringRef)@" ",
                                                                                 (CFStringRef)@":/?@!$&'()*+,;=",
                                                                                 kCFStringEncodingUTF8));
    return [result stringByReplacingOccurrencesOfString:@" " withString:@"+"];
}

Note, there was a promising NSString method, stringByAddingPercentEscapesUsingEncoding (now deprecated), that does something very similar, but resist the temptation to use that. It handles some characters (e.g. the space character), but not some of the others (e.g. the + or & characters).

The contemporary equivalent is stringByAddingPercentEncodingWithAllowedCharacters, but, again, don't be tempted to use URLQueryAllowedCharacterSet, as that also allows + and & pass unescaped. Those two characters are permitted within the broader "query", but if those characters appear within a value within a query, they must escaped. Technically, you can either use URLQueryAllowedCharacterSet to build a mutable character set and remove a few of the characters that they've included in there, or build your own character set from scratch.

For example, if you look at Alamofire's parameter encoding, they take URLQueryAllowedCharacterSet and then remove generalDelimitersToEncode (which includes the characters #, [, ], and @, but because of a historical bug in some old web servers, neither ? nor /) and subDelimitersToEncode (i.e. !, $, &, ', (, ), *, +, ,, ;, and =). This is correct implementation (though you could debate the removal of ? and /), though pretty convoluted. Perhaps CFURLCreateStringByAddingPercentEscapes is more direct/efficient.

Rob
  • 371,891
  • 67
  • 713
  • 902
  • True about `stringByAddingPercentEscapesUsingEncoding:`, but `stringByAddingPercentEncodingWithAllowedCharacters:` works fine with every character. Example: `URLString = [URLString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];` – Alejandro Cotilla Feb 05 '16 at 21:39
  • @AlejandroDavidCotillaRojas - You can use `stringByAddingPercentEncodingWithAllowedCharacters`, but you cannot use `URLQueryAllowedCharacterSet`, as that includes `+` and `&` (those are permitted character within the broader query, but if those characters appear within a value within a query, they must escaped, which that character set won't do). So, you can either use `URLQueryAllowedCharacterSet` to build a mutable character set and remove a few of the characters that they've included in there, or build your own character set from scratch. Or use `CFURLCreateStringByAddingPercentEscapes`. – Rob Feb 05 '16 at 21:59
  • Great answer, really makes the case for using Alamofire to avoid all this depth! – Dan Rosenstark Mar 03 '16 at 01:13
  • Given that `CFURLCreateStringByAddingPercentEscapes` is now deprecated, you really should build the appropriate character set yourself. See https://stackoverflow.com/a/54742187/1271826. – Rob Feb 18 '19 at 07:50
8

The example code above was really helpful to me, however (as has been hinted at above), I think you need to use NSMutableURLRequest rather than NSURLRequest. In its current form, I couldn't get it to respond to the setHTTPMethod call. Changing the type fixed things right up.

Hemang
  • 25,740
  • 17
  • 113
  • 171
David Fulton
  • 493
  • 4
  • 13
8
 NSURL *url= [NSURL URLWithString:@"https://www.paypal.com/cgi-bin/webscr"];
 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl
                                                        cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                    timeoutInterval:10.0];
[request setHTTPMethod:@"POST"];
 NSString *postString = @"userId=2323";
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
Alex Terente
  • 11,770
  • 5
  • 47
  • 66
1

Any one looking for a swift solution

let url = NSURL(string: "http://www.apple.com/")
let request = NSMutableURLRequest(URL: url!)
request.HTTPBody = "company=Locassa&quality=AWESOME!".dataUsingEncoding(NSUTF8StringEncoding)
spaceMonkey
  • 3,805
  • 4
  • 20
  • 33