ios URL Encoding

When transmitting data in the context of the HTTP protocol you often need to encode text in a way that does not interfere with special characters used in URLs. This is of importance if you want to put unicode characters into an URL query but also for simple things like constructing a body for a HTTP POST request. A form post also takes the form fields and puts them into the form that you know from an URL: field=text&another=more. That’s what the HTML content type “application/x-www-form-urlencoded” means.

The first thing that jumps out of the documentation when looking for a standard function to achieve such “URL Encoding” is stringByAddingPercentEscapesUsingEncoding. So that is what I was using for encoding the password for my iTunes Connect class which drives MyAppSales. And until now this worked without a hitch until customer #113 who was the first to use a plus character in his password. The poor guy ended up locking his iTunes account. Sorry!

It turns out that + is an anachronistic special character substitution for a space. I would have expected for it to be encoded properly by the above mentioned method as %20, but this is not the case.

 

This you can verify for yourself by this experiment:

NSString *password = @"Top+Secret. ";
NSString *encoded_normal = [password stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *encoded_safer = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,  (CFStringRef)password,  NULL,  (CFStringRef)@"!*'();:@&=+$,/?%#[]",  kCFStringEncodingUTF8);
 
// output with %@ otherwise the % escapes cause strange output
NSLog(@"%@", password);
NSLog(@"%@", encoded_normal);
NSLog(@"%@", encoded_safer);

You will find that the + does not get encoded by stringByAddingPercentEscapesUsingEncoding. This example also contains a solution for the dilemma. CoreFoundation provides a cousin of the NSString instance method which also allows to specify characters for which you want to forced encoding even though they are deemed safe. So-called “toll free bridging” allows us to simple typecast CFStringRef into NSString and vice versa. This does the trick.

Thanks to Andrew Nicolle who pointed this out to me in completely unrelated circumstances. Your solution has wider reaching consequences than anticipated! ;-)

Obviously it’s smart to put the above mentioned method together with all the other NSString helper methods so that you can save yourself unnecessary duplication of code.

NSString+Helpers.h

#import 
 
@interface NSString (Helpers)
 
// helper functions
- (NSString *) stringByUrlEncoding;
 
@end

NSString+Helpers.m

#import "NSString+Helpers.h"
 
@implementation NSString (Helpers)
 
#pragma mark Helpers
- (NSString *) stringByUrlEncoding
{
	return (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,  (CFStringRef)self,  NULL,  (CFStringRef)@"!*'();:@&=+$,/?%#[]",  kCFStringEncodingUTF8);
}
 
@end

Finally, here is an example of building and sending a HTTP POST that uses this method. If somebody knows of a more elegant way to construct one please let me know. The regular NSURLRequest is a GET and the only way I found that allowed me to change the verb to POST was to use an NSMutableURLRequest instead. This intentionally omits the 4 necessary call-back methods of the NSURLConnectionDelegate protocol for brevity.

// #import for NSString+Helpers.h at the top
 
NSMutableURLRequest *theRequest=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://server/post_url"]
								   cachePolicy:NSURLRequestUseProtocolCachePolicy
							   timeoutInterval:30.0];
 
[theRequest setHTTPMethod:@"POST"];
[theRequest addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField: @"Content-Type"];
 
//create the body
NSMutableData *postBody = [NSMutableData data];
[postBody appendData:[[NSString stringWithFormat:@"name=%@&password=%@",
					   [username stringByUrlEncoding],
					   [password stringByUrlEncoding]] dataUsingEncoding:NSUTF8StringEncoding]];
[theRequest setHTTPBody:postBody];
 
NSURLConnection *theConnection=[[[NSURLConnection alloc] initWithRequest:theRequest delegate:self] autorelease];

Coming to think of it, if there really is no smarter way to make an asychronous HTTP POST then maybe I should put all of this into a category for NSURLRequest. But that’s another story.

 

 

 

from http://www.cocoanetics.com/2009/08/url-encoding/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值