5

I have created one user enquiry form in ios. I'm using php as a server side. I constructed query string like bellow in IOS

http://www.mydomain.in/androidmail_enquiry.php?name=Vinoth Kumar&phone=04259280244&email=vinoth@ldomain.com&address=Coimbatore&comments=Sample Enquiry&mobile=xxxxxxxx

Code:

-(void)sendEnquiryDetails
{
    cmttextview.text = @"Sample Enquiry";
    NSString *siteurl = @"http://www.mydomain.in/androidmail_enquiry.php?";
    NSString *name = txtName.text;
    NSString *phone = txtPhone.text;
    NSString *email = txtEmail.text;
    NSString *address = txtAddress.text;
    NSString *comments = cmttextview.text;
    NSString *mobile = txtMobile.text;
    NSString *enquiryurl = [NSString stringWithFormat:@"%@name=%@&phone=%@&email=%@&address=%@&comments=%@&mobile=%@",siteurl,name,phone,email,address,comments,mobile];

    NSLog(enquiryurl);

   NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:enquiryurl]];
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request  delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    //NSLog(@"didReceiveResponse");
    [self.responseData setLength:0];
}

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [self.responseData appendData:data];
}

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"%@",[NSString stringWithFormat :@"didfailwitherror: %@", [error description]]);
}

-(void)connectionDidFinishLoading: (NSURLConnection *)connection{
    NSLog(@"Success Code:%@",self.responseData);
}

But when i submit this by using NSURLRequest getting error like invalid url(URL Forming error) in didFailWithError method.

Vinoth Kumar
  • 489
  • 3
  • 17
  • 45

3 Answers3

7

Make sure You have added stringByAddingPercentEscapesUsingEncoding , because in your url there is space in name , so in browser it is replaced with %20 you have to add programmatically in code,

Try this

-(void)sendEnquiryDetails
{
    cmttextview.text = @"Sample Enquiry";
    NSString *siteurl = @"http://www.mydomain.in/androidmail_enquiry.php?";
    NSString *name = txtName.text;
    NSString *phone = txtPhone.text;
    NSString *email = txtEmail.text;
    NSString *address = txtAddress.text;
    NSString *comments = cmttextview.text;
    NSString *mobile = txtMobile.text;
    NSString *enquiryurl = [NSString stringWithFormat:@"%@name=%@&phone=%@&email=%@&address=%@&comments=%@&mobile=%@",siteurl,name,phone,email,address,comments,mobile];

    NSLog(enquiryurl);

 NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[enquiryurl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request  delegate:self];
}
Toseef Khilji
  • 17,192
  • 12
  • 80
  • 121
  • Thanks i corrected my mistake, now it's working by added stringByAddingPercentEscapesUsingEncoding – Vinoth Kumar Oct 10 '13 at 04:46
  • 4
    @VinothKumar This only works if the password (and any of the other fields) do not contain any of the other reserved characters (e.g. `&`, `+` and others). You should use `CFURLCreateStringByAddingPercentEscapes` if you want this to be more robust. – Rob Oct 10 '13 at 13:13
4

You have to percent escape reserved characters. If nothing else, you have to replace those spaces with + (or %20).

Unfortunately, the standard stringByAddingPercentEscapesUsingEncoding is not up to the job, as it leaves some characters unescaped. Your sample doesn't include any of them, but if the person's name was entered as "Bill & Melinda Gates" or "Bill + Melinda Gates", stringByAddingPercentEscapesUsingEncoding would not handle that properly. You really should use CFURLCreateStringByAddingPercentEscapes to percent escape the values, for example using this category:

@implementation NSString (URLEncode)

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

@end

This properly escapes all reserved characters, but replaces spaces with + characters (which you should do if your request type is application/x-www-form-urlencoded).

Alternatively, you can also replace the space characters with %20 like so:

- (NSString *)stringForHTTPRequest
{
    return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                     (CFStringRef)self,
                                                                     NULL,
                                                                     (CFStringRef)@":/?@!$&'()*+,;=",
                                                                     kCFStringEncodingUTF8));
}

With that, you could modify that line of code that builds the URL string:

NSString *enquiryurl = [NSString stringWithFormat:@"%@name=%@&phone=%@&email=%@&address=%@&comments=%@&mobile=%@",
                        siteurl, 
                        [name stringForHTTPRequest],
                        [phone stringForHTTPRequest],
                        [email stringForHTTPRequest],
                        [address stringForHTTPRequest],
                        [comments stringForHTTPRequest],
                        [mobile stringForHTTPRequest]];
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Finally, I can agree with most of your statements. :) The "application/x-www-form-urlencoded" encoding algorithm *can* be implemented using `CFURLCreateStringByAddingPercentEscapes` and `stringByReplacingOccurrencesOfString:` the way you suggested, just with a slightly *different* set of `legalURLCharactersToBeEscaped`. But eventually, after decoding, both resulting strings are equal again anyway. I will edit may answer and remove the other irrelevant stuff. – CouchDeveloper Oct 10 '13 at 12:58
  • @Rob AFNetworking converts the characters `:/?&=;+!@#$()',*` are you missing some \? – David Snabel-Caunt Oct 10 '13 at 13:35
  • 1
    @DavidCaunt Yes, that is correct. That escapes all of the reserved characters listed in RFC 3986 (though the `#` is not needed, because that's already escaped). I've updated my answer accordingly. – Rob Oct 10 '13 at 14:10
  • What's about the `*` which is element of the `sub-delims` set? According RFC 3986, in a query component, characters from the `sub-delims` set don't need to be encoded. The "application/x-www-form-urlencoded" does also not require to encode it. :) – CouchDeveloper Oct 10 '13 at 17:07
  • @CouchDeveloper I agree that you might not have to escape the `sub-delim`, but I see no reason not to, and it seems more future-proof to include it. I don't know why you'd escape some of the `sub-delim`, but single out the asterisk for exclusion. If this still troubles you, I'd suggest you ask a new S.O. question on the topic, or submit an [issue to AFNetworking](https://github.com/AFNetworking/AFNetworking/issues) and ask there. But the convention of AFNetworking (which I've adopted here) is to escape all of the RFC3986 reserved characters. – Rob Oct 10 '13 at 20:34
  • Well, regarding "x-www-form-urlencoded", I want to be as close as possible to the w3c recommendation draft, which is by the way up to date: the suggested algorithm is from August, 2013. The RFC 3986 is from January 2005. I don't know what to do in this case, since the w3c algorithm is defined for _forms_, not for _URLs_, but nonetheless is in wide spread use for encoding of the query component. I think the best what we can get are _opinions_ of what might be the best solution. And lastly, it doesn't matter anyway - except perhaps for some unwary decoder. – CouchDeveloper Oct 10 '13 at 21:09
  • Yeah, don't use RFC 3986. Go with spec W3C: HTML5 Forms: URL-encoded form data: application/x-www-form-urlencoded encoding algorithm: http://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm. For a swift implementation, see [application/x-www-form-urlencoded encoding algorithm Swift iOS](http://stackoverflow.com/a/26317562/242933). – ma11hew28 Jun 18 '15 at 18:23
2

According the docs, if a NSURL object is initialized with a string, the string must conform to RFC 2396 (see official Apple docs URLWithString).

The most recent RFC however is RFC 3986 which obsoletes RFC 2396.

Caution:

Neither Cocoa nor Core Foundation will have a function which out of the box returns a properly encoded URL string conforming to RFC 2396 or RFC 3986, though.

Different components of a URL have varying percent encoding requirements.

Edit:

In order to transmit structured parameters in a unstructured query component of a URL, an encoding scheme equal to the application/x-www-form-urlencoded encoding algorithm can be applied to the query component.

A helper function which encodes the name or value and which conforms to that encoding can be implemented as:

static NSString* urlformdata_encode(NSString* s) {
    CFStringRef charactersToLeaveUnescaped = CFSTR(" ");
    CFStringRef legalURLCharactersToBeEscaped = CFSTR("!$&'()+,/:;=?@~");
    
    NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
                             kCFAllocatorDefault,
                             (__bridge CFStringRef)s,
                             charactersToLeaveUnescaped,
                             legalURLCharactersToBeEscaped,
                             kCFStringEncodingUTF8));
    return [result stringByReplacingOccurrencesOfString:@" " withString:@"+"];
}

(Note: this code depends on the current implementation of CFURLCreateStringByAddingPercentEscapes, especially its default escaping. If those change, the code may not correctly implement the algorithm.)

According RFC 3986, the query component of the URL is treated as an unstructured percent encoded byte stream containing only ASCII characters.

Originally, the "application/x-www-form-urlencoded" encoding scheme has been defined for transmitting form-data from a browser to a server which becomes the payload of a body. That is, the context is totally different than for URLs. Nonetheless, even this encoding scheme is not defined in RFC 3986, it is finally compatible with the URL encoding scheme.

Caveats

The "application/x-www-form-urlencoded" encoding scheme may produce percent escapes for characters that are now in the "Unreserved" character set according RFC 3986, namely the tilde ~ character. Characters in the "Unreserved" character set don't need to be escaped. More strictly, due to implications in URI comparison implementations, RFC 3986 makes this recommendation:

"For consistency, percent-encoded octets in the ranges of ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should not be created by URI producers".

In order to not escape the tilde ~ in query parameters, one can remove the tilde ~ from the set of legal URL characters to be escaped legalURLCharactersToBeEscaped:

CFStringRef legalURLCharactersToBeEscaped = CFSTR("!$&'()+,/:;=?@");

Not escaping the tilde should not cause issues, even though it is not strict according the "application/x-www-form-urlencoded encoding" algorithm.

Differences to the other suggested algorithms to this question:

Since the "application/x-www-form-urlencoded" decoding algorithm decodes any percent decoded character sequence into its corresponding Unicode code unit, differences in the set of legalURLCharactersToBeEscaped specified in CFURLCreateStringByAddingPercentEscapes during encoding may eventually yield equal strings.

(Edit: removed helper functions which encode components of a URL, which are not relevant when a query string should be encoded via application/x-www-form-urlencoded)

Community
  • 1
  • 1
CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67
  • We should probably not use that ancient "application/x-www-form-urlencoded" encoding in URL params. It's perfectly valid to use JSON for example, and percent encode that according a query component defined in RFC 3986. – CouchDeveloper Oct 10 '13 at 16:21
  • @Rob Thanks for hint about the tilde! Added a "Caveat" section explaining the issue. – CouchDeveloper Oct 10 '13 at 16:47