iOS网络编程读书笔记
Facade Tester客户端门面模式的实例(被动版本化)
被动版本化,所以硬编码URL.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
FTAppDelegate *appDelegate = (FTAppDelegate*)[[UIApplication sharedApplication] delegate];
if (appDelegate.urlForWeatherVersion1 != nil) {
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:appDelegate.urlForWeatherVersion1
options:NSDataReadingUncached
error:&error];
if (error == nil) {
NSDictionary *weatherDictionary = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableLeaves
error:&error];
if (error == nil) {
v1_city = [weatherDictionary objectForKey:@"city"];
v1_state = [weatherDictionary objectForKey:@"state"];
v1_temperature = [weatherDictionary objectForKey:@"currentTemperature"];
// update the table on the UI thread
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
} else {
NSLog(@"Unable to parse weather because of error: %@", error);
[self showParseError];
}
} else {
[self showLoadError];
}
} else {
[self showLoadError];
}
});
}
通过GCD在global线程中加载网络请求,用到的是[NSData dataWithContensOfURL:]; 数据用SJSONSerialization JSONObjectWithData:data..]进行序列化然后就能使用了。
在应用委托中定义可以使得在实现服务定位器时更具有灵活性
#import
@interface FTAppDelegate : UIResponder
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UITabBarController *tabBarController;
// normally you wouldn't put these here!
@property (strong, nonatomic) NSURL *urlForWeatherVersion1;
@property (strong, nonatomic) NSURL *urlForWeatherVersion2;
@property (strong, nonatomic) NSURL *urlForStockVersion1;
@property (strong, nonatomic) NSURL *urlForStockVersion2;
@end
由于大多数Web Service 都会将结果以JSON的形式输出,因此使用JSON来表示服务定位器比较好。服务定位器用来探测天气与股票报价API端点。该结构将端点的所有版本都组合到了一个文件中。
在应用启动和返回到前台时加载服务定位器,将URL存储为应用委托中的属性.(更复杂的应用则需要专门的网络管理器,由它处理服务定位器的加载,其他viewcontroller 也会使用它针对特定的网络调用查询端点(api))
- (void)loadServiceLocator {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
#warning Replace the following line with your own domain and path to the service locator
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://example.com/api/serviceLocator.json"]
options:NSDataReadingUncached
error:&error];
if (error == nil) {
NSDictionary *locatorDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error];
if (error == nil) {
self.urlForStockVersion1 = [self findURLForServiceNamed:@"stockQuote" version:1 inDictionary:locatorDictionary];
self.urlForStockVersion2 = [self findURLForServiceNamed:@"stockQuote" version:2 inDictionary:locatorDictionary];
self.urlForWeatherVersion1 = [self findURLForServiceNamed:@"weather" version:1 inDictionary:locatorDictionary];
self.urlForWeatherVersion2 = [self findURLForServiceNamed:@"weather" version:2 inDictionary:locatorDictionary];
} else {
NSLog(@"Unable to parse service locator because of error: %@", error);
// inform the user on the UI thread
dispatch_async(dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:@"Error"
message:@"Unable to parse service locator."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
});
}
} else {
NSLog(@"Unable to load service locator because of error: %@", error);
// inform the user on the UI thread
dispatch_async(dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:@"Error"
message:@"Unable to load service locator. Did you remember to update the URL to your own copy of it?"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
});
}
});
}
这个json运行于服务器上,应用就能请求到serviceLocator并解析.
HTTP协议
一个URL只对应一个资源,多个URL可对应同一个资源.(localhost是个例外)
URL由5部分构成:
http://user:password@hostname:port/absolute-path?query
协议 认证 主机名 端口 绝对路径 查询字符串
协议
除了HTTP外还可以使用FTP协议。iOS应用中常用的另外一种协议还有FILE ,用于请求在应用sandbox中的本地资源,如果使用字符串而没有使用协议创建NSURL对象,那么默认就会使用FILE协议。
端口
HTTP默认端口是80,HTTPS默认端口443(有些网络代理和防火墙会处于安全或者隐私考虑等原因阻塞非标准端口)
绝对路径
很多REST服务使用路径来传递值,用来唯一标识数据库中存储的实体。比如/customer/456/address/0指定索引为0的地址,具有标识456的用户.
查询字符串
多个查询参数用&字符分隔,查询字符串不可以包含回车、空格与换行符。
iOS为NSString对象提供了一个方法来执行URL的百分号编码
NSString *urlString = @"http://myhost.com?query=This is a question";
NSString *encoded = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
结果为http://myhost.com?query=This%20is%20a%20question.该编码不同于URL编码,不会对&字符编码,因此也不会改变URL参数的分隔.URL编码会编码& ? 和其他标点符号,如果查询的字符串包含了这些字符,那么需要实现一种更为彻底地编码方法。
请求内容
HTTP请求包含三部分:请求行、请求头与请求体。请求行和请求头是文本行,通过回车/换行符分隔(值为13的字节,或是0x0D/值为10的字节,或是0x0A).在HTTP请求中这样使用文本值,使得它们很容易构建、解析和调试。空行(仅包含回车/换行符或是仅有换行符)将请求头与请求体分隔开。
HTTP请求实例:
GET /search?source=ig&h1=en&rlz=&q=ios&btnG=Google+Search HTTP/1.1
HOST: www.google.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:11.0)...
Accept: text/html,application/xhtml+xml,application/xml:q=0.9,*/*;q=0.8
Accept-Language: en,en-us;q=0.7,en-ca;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://www.google.com/ig?hl=en&source=webhp
Cookie: PREF=ID=fdf9979...
请求行是发送给服务器的第一行数据。请求行包括3方面主要信息:HTTP请求方法、请求URI与HTTP版本.
标准请求方法都是大写。
GET
从服务器获取一段内容(用HTTP术语来说,就是实体entity),不会导致服务器端的数据发生变化,GET请求通常不包括请求体,不过也可以包含。
POST
使用客户端提供的数据更新实体.POST请求通常会在请求体中加入应用服务器
(运行php等?)所需的信息.POST请求是非幂等(待查)的.这意味着如果处理多个请求,那么结果与处理单个请求是不同的。
HEAD
获取响应的元数据而无需检索响应的全部内容。该方法通常用于检查服务器最近的内容变化而无须检索全部内容
PUT
使用客户端提供的数据添加实体。PUT请求通常将应用服务器所需的信息放在请求体中来创建新的实体。在通常情况下,PUT请求是幂等的,这意味着多个请求的处理会产生相同的结果。
DELETE
根据URI的内容或客户端提供的请求体来删除实体。DELETE请求是REST服务接口中使用最为频繁的请求。
说明:
HTTP规范允许HTTP客户端与服务器之间的中介添加、删除、重排序以及修改HTTP头。因此,应用发出的请求在到达服务器时可能会出现以下的情况:添加新的头,修改已有的头或者删除某些头。
虽然使用了有状态的TCP传输层,但HTTP却是个无状态协议。这意味着HTTP服务器并不会保留关于某个请求的任何信息以用在未来的请求中。Cookie提供了一种方式,可以将一些简单地状态信息存储在客户端,并在后续的请求中与服务器进行通信。
HTTP头之后是可选的请求体。请求体可以使任意的字节序列,通过一个空行与头分割开来,请求体必须遵循客户端与服务器之间预先确定的数据编码。对于Web浏览器来说,这通常是表单编码的数据,但对于移动应用来说,通常是JSON数据。在iOS中NSURLRequest及其子类NSMutableURLRequest提供了必要的方法与属性来构建几乎所有的HTTP请求。
在HTTP服务器与应用服务器处理完请求后,HTTP响应会通过同一个TCP socket[待查]返回给客户端.HTTP响应的结构类似于HTTP请求,第一行也是状态行,后面是头,然后是响应体。
简单的HTTP响应示例:
HTTP/1.1 200 OK
Date: Tue, 27 Mar 2012 12:25;12 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=UTF-8
Content-Encoding: gzip
Transfer-Encoding: chunked
Server: gws
ios - Google Search