10

I'm doing an HTTP Post in my iphone app and one of the parameters I send to the server is a URL. The problem is that when I convert from an NSURL to an NSURLRequest, the string http://www.slashdot.org becomes http:/www.slashdot.org (one of the forward slashes is missing)

is there a way around this?

here is the code I'm using:

NSString *host = @"example.host.com";
NSString *urlString = [NSString stringWithFormat:@"/SetLeaderUrl.json?leader_email=%@&url=%@",localEmail,urlToPublish];
NSURL *url = [[NSURL alloc] initWithScheme:@"http" host:host path:urlString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *jsonString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];

I've used NSLog to see where it loses the '/' and it's on the fourth line:

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

thanks for taking the time to read!

vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
Lance
  • 8,872
  • 2
  • 36
  • 47
  • I find that the slash is lost when creating the NSURL from the NSString, not when going from the NSURL to NSURLRequest. You can see that if you NSLog([url description]). – Oscar Feb 26 '13 at 07:35

3 Answers3

12

You're not percent-escaping the query values before substituting them in to the string. I just did a little test, and found that if I set urlToPublish to "http://example.com", then NSURL would transform it into "http:/example.com".

This is because the query value contains special characters, which means you need to add percent escapes. At the very least you can use the mediocre -[NSString stringByAddingPercentEscapesUsingEncoding:] with the NSASCIIStringEncoding. Far better would be to use a different (and more complete) escaping mechanism, such as the one I suggest in this post.


In this case, stringByAddingPercentEscapesUsingEncoding: does not work, because it's a pretty lousy method. It works on an inclusive model, which means you have to tell it which characters you want percent encoded. (Under the hood, it's just calling CFURLCreateStringByAddingPercentEscapes()) This function basically asks you for a string that represents every character it's allowed to percent-encode (as I understand the function). What you really want is an exclusive model: escape everything except [this small set of characters]. The function I linked to above does that, and you'd use it like this:

NSString *urlToPublish = [@"http://stackoverflow.com" URLEscapedString_ch];
NSString *host = @"example.host.com";
NSString *urlString = [NSString stringWithFormat:@"/SetLeaderUrl.json?leader_email=%@&url=%@",localEmail,urlToPublish];
NSURL *url = [[NSURL alloc] initWithScheme:@"http" host:host path:urlString];

And then it will build your URL properly.


Here's another way you could do this (and do it correctly). Go to my github page and download "DDURLBuilder.h" and "DDURLBuilder.m", and then build your URL like this:

NSString *localEmail = @"foo@example.com";
NSString *urlToPublish = @"http://stackoverflow.com"

DDURLBuilder *b = [DDURLBuilder URLBuilderWithURL:nil];
[b setScheme:@"http"];
[b setHost:@"example.host.com"];
[b setPath:@"SetLeaderUrl.json"];
[b addQueryValue:localEmail forKey:@"leader_email"];
[b addQueryValue:urlToPublish forKey:@"url"];

NSURL *url = [b URL];
Community
  • 1
  • 1
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • @Lance as I said, `stringByAddingPercentEscapesUsingEncoding:` is mediocre and, in this case, does not work. Edited answer. – Dave DeLong Mar 24 '11 at 17:05
  • Thanks dave :) I'm trying to implement your method from the other post, and I put the method declaration in the header file like this: -(NSString *)URLEncodedString_ch; but when I try to call the method in my .m file, just like you have it except it says encoded instead of escaped, it gives me a warning that it may not respond. what am I doing wrong? – Lance Mar 24 '11 at 17:38
  • @Lance the code in the other post is meant to be an `NSString` category. I just finished editing my post with a different alternative. – Dave DeLong Mar 24 '11 at 17:40
  • @Dave You are amazing! thanks so much for putting the time into helping me out. this worked like a charm :) – Lance Mar 24 '11 at 17:58
  • @Lance you're welcome! Sorry to be so vehement about this, but I've learned through hard experience that almost no one gets this right. – Dave DeLong Mar 24 '11 at 18:01
  • @Dave including apple (or whoever wrote the stringByAddingPercentEscapesUsingEncoding: method) thanks again! – Lance Mar 24 '11 at 18:18
-2

Here is some code apple use to get a NSURLRequest,

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

@Dave DeLong: I notice in the Apple "URL Loading System Program Guide" the example creating a connection and request does not use any escaping. The Url it uses is from a NSURL URLWithString:

miken32
  • 42,008
  • 16
  • 111
  • 154
markhunte
  • 6,805
  • 2
  • 25
  • 44
  • @markhunte the problem is not escaping `@"example.host.com"`, the problem is escaping `http://example.com`. You *do* have the problem that the OP is reporting. Look at your log output. It has `"url=http:/example.com"` instead of `"url=http://example.com"` (which is what the OP is expecting, but which would be wrong). It *should* be `"url=http%3A%2F%2Fexample.com"` – Dave DeLong Mar 24 '11 at 01:47
  • @ Dave DeLong, yes you are correct. My bad, Embarrassed to say I did not look at my own output closely enough. So the Problem is actually with if you use the initWithScheme:host:path: for you NSURL. – markhunte Mar 24 '11 at 07:05
  • @markhunte yea, it is a problem with that method and I still haven't found a solution, I've tried using the stringByReplacingOccurrencesOfString:@:"//" withString@"/\/" or with @"%2F%2F" and neither of those work.... – Lance Mar 24 '11 at 17:01
  • The problem is not `NSURL`. The problem is not correctly percent-encoding the string before giving it to `NSURL`. The documentation for `NSURL` says that the path must conform to RFC 2396, which means any special characters *must be percent encoded before ever giving them to an `NSURL` initializer*. In other words, the problem is not `NSURL`; the problem is not understanding how to correctly make a URL. – Dave DeLong Mar 24 '11 at 17:13
  • @Dave DeLong, When you use the other methods of NSURL to the / is not stripped. Can you please explain what the difference is then. That you say its not this particular method that is a odds. Also in the guide I referred to, apple do not do anything special for forming the request. I have put the code in my above answer as I can not put it here.. – markhunte Mar 24 '11 at 18:05
  • @markhunte the difference is that the query string and path portion of the URL are treated differently from the host. – Dave DeLong Mar 24 '11 at 18:16
  • @Dave DeLong,Thank you for that, I see what you mean, I have been looking at the path, which even in the NSURL initWithScheme: method the path is automatically escaped using the stringByAddingPercentEscapesUsingEncoding: method, but the host as you say is treated differently. – markhunte Mar 24 '11 at 18:35
  • I would add that the problem is the difficulty of understanding and getting RFC compliance correct coupled with the poor API putting the burden on the API consumer rather than making it easier and clearer to do right. – uchuugaka Mar 16 '15 at 07:48
-5

I fixed it, this is what I had to do:

NSString *urlString = [NSString stringWithFormat:@"/SetLeaderUrl.json?leader_email=%@&url=%@",localEmail,urlToPublish];
urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
urlString = [NSString stringWithFormat:@"%@%@%@",scheme,host,urlString];
NSURL *url = [[NSURL alloc] initWithString:urlString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
Lance
  • 8,872
  • 2
  • 36
  • 47
  • 7
    -1000000 NO NO NO NO NO. THIS IS ABSOLUTELY WRONG. You are only supposed to encode **the keys and the values of the query string**, not the entire path. You are creating an ILLEGAL URL this way, and the fact that it still works is an happy coincidence, but it will most likely break in the future. **Do it right, or don't do it at all.** – Dave DeLong Mar 24 '11 at 17:30