Here’s a surprisingly important note from the documentation for the NSURL
class’ path
method:
Return Value
The path of the URL, unescaped with the
stringByReplacingPercentEscapesUsingEncoding:
method. If the receiver does not conform to RFC 1808, returnsnil
. If this URL is a file URL (as determined withisFileURL
), the return value is suitable for input into methods of NSFileManager or NSPathUtilities. If the path has a trailing slash it is stripped.
Trailing Slashes
Trailing slashes in URLs are sort of funny things; whether URLs should include them or not is a matter of some debate, determined in practice by the conventions of the framework used to implement the server at the endpoint of the URL. For instance, Django strongly suggests (although it doesn’t really require, due to all those regexes) that all your URLs end with trailing slashes.
Example
Let’s say you’re dealing with URLs that include trailing slashes. And let’s say you use NSURLs
to reach them, with something like this:
// Set up request ...
NSURL* url = [NSURL URLWithString:@"http://www.example.com/foo/bar/baz/"]
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30];
request.HTTPMethod = @"POST";
// Set up delegate
MyNSURLConnectionDelegate* delegate = … set up an appropriate object …
// Create and start request
NSURLConnection* conn = [[[NSURLConnection alloc] initWithRequest:request
delegate:delegate] autorelease];
[conn start];
This will produce an HTTP request on the wire that looks something like this:
POST /foo/bar/baz/ HTTP/1.1
Host: www.example.com
On the other hand, url.path
will return /foo/bar/baz
. The path returned by url.path
is not the same as the path sent over the wire.
Signing
“So what?”, you might ask. Well, the less-important point here is that /foo/bar/baz
and /foo/bar/baz/
are not the same thing, and don’t (in principle) refer to the same object. (In practice, they’re often hacked to be synonyms on the server-side, but that’s not an assumption that a client should really be making.) The more important point is that if you’re signing your request (with, e.g., OAuth), then you need to know the exact path sent over the wire.
The slash-stripping behavior of path
hides the precise path sent over the wire. If you’re handed an NSMutableURLRequest
and asked to sign it (by, e.g., appending an OAuth authorization header) you can’t use the path
method to construct the signature without some other information that specifies whether or not the path on the wire ends with a slash.
FWIW, absoluteString
does not strip trailing slashes. This is helpful, but complicated by the fact that other parts of the URL (e.g., the query string) follow the path; a trailing slash won’t necessarily be the last character in a URL’s absoluteString
.
This is all a little inconvenient.