iOS-----使用AFNetworking实现网络通信
2015-12-24 14:27
447 查看
使用AFNetworking实现网络通信
AFNetworking可以用于发送HTTP请求,接收HTTP响应,但不会缓存服务器响应,不能执行HTML页面中嵌入的JavaScript代码,也不会对页面内容进行任何解析、处理,内置支持JSON、Plist属性文件、XML解析。
提交GET请求与提交POST请求
在需要使用AFNetworking的程序中使用“#import “AFHTTPRequestOperationManager.h””代码导入AFNetworking的头文件即可使用AFNetworking发送请求、接收响应很简单,只要如下几步 |
1.创建AFHTTPRequestOperationManager对象 |
2.根据服务器响应内容的不同,为AFHTTPRequestOprationManager对象指定不同的解析器。 该对象默认的解析器要求服务器响应是JSON数据或Plist数据。 如果服务器响应数据是其他格式的,则需要手动设置响应解析器。 |
3.如果需要发送GET请求,调用AFHTTPRequestOperationManager对象的GET:parameters:success:failure:方法即可; 如果要发送POST请求,调用该对象的POST:parameters:success:failure:方法即可.两个方法都可指定通信成功、通信失败的代码块。 |
4.在success:参数指定的代码块中处理服务器响应成功的正确数据;在failure:参数指定的代码块中处理服务器响应的错误数据。 |
代码片段 |
/* 为了通过AFNetworking来访问被保护页面,程序同样需要使用AFNetworking来登录系统, 只要应用程序使用同一个AFHTTPRequestOperationManager发送请求, AFNetworking就会自动维护与服务器之间的Session状态。也就是说,程序第1次使用AFHTTPRequestOperationManager登录系统后, 接下来使用该对象杰克访问被保护页面了。 */ ViewController.m @interface ViewController() { AppDelegate* appDelegate; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; appDelegate = [UIApplication sharedApplication].delegate; } - (IBAction)accessSecret:(id)sender { // 使用AFHTTPRequestOperationManager发送GET请求 [appDelegate.manager GET:@http://192.168.1.88:8888/AFNetworkingServer/secret.jsp parameters:nil // 无须指定请求参数 // 获取服务器响应成功时激发的代码块 success:^(AFHTTPRequestOperation *operation , id responseObject) { // 当使用HTTP响应解析器时,服务器响应数据被封装在NSData中 // 此处将NSData转换成NSString, 并使用UIWebView将响应字符串显示除了出来 [self.showView loadHTMLString:[[NSString alloc] initWithData: responseObject encoding:NSUTF8StringEncoding] baseURL:nil]; } // 获取服务器响应失败时激发的代码块 failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@”获取服务器响应出错! ”); }]; } @end |
上面程序中应用程序委托的manager代表一个已经初始化完成的AFHTTPRequestOperationManager对象, 需要修改应用程序委托的application:didFinishLaunchingWithOptions:方法,保证在该方法中创建AFHTTPRequestOperationManager对象, 并指定服务器响应的解析器。 |
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { // 创建AFHTTPRequestOperationManager对象 self.manager = [AFHTTPRequestOperationManager manager]; // 为AFHTTPRequestOperationManager对象指定使用HTTP响应解析器 self.manager.responseSerializer = [[AFHTTPResponseSerializer alloc] init]; return YES; } /* 创建了一个AFHTTPRequestOperationManager对象,接下来为该对象指定使用AFHTTPResponseSerializer响应解析器, 负责解析服务器响应的HTML数据,当使用这种响应解析器时,代表服务器响应的对象是NSData类型的数据.. */ |
LoginController.m @interface LoginController() { AppDelegate* appDelegate; } @end @implementation LoginController - (void)viewDidLoad { [super viewDidLoad]; appDelegate = [UIApplication sharedApplication].delegate; } - (IBAction)loginBnClicked:(id)sender { // 获取用户输入的用户名和密码 NSString* name = self.nameFiled.text; NSString* pass = self.passField.text; if(name != nil && name.length > 0) { // 使用NSDictionary封装请求参数 NSDictionary* params = @{@”name”: name , @”pass”: pass}; // 使用AFHTTPRequestOperationManager发送POST请求 [appDelegate.manager 46 47 POST:@http://192.168.1.88:8888/AFNetworkingServer/login.jsp 48 49 parameters:params // 指定请求参数 50 51 // 获取服务器响应成功时激发的代码块 52 53 success:^(AFHTTPRequestOperation * operation, id responseObject) 54 55 { 56 57 // 当使用HTTP响应解析器时,服务器响应数据被封装在NSData中 58 59 // 此处将NSData转换成NSString,并使用UIAlertView显示登录结果 60 61 [[[UIAlertView alloc] initWithTitle:@” 登录结果 ” message: 62 63 [[NSString alloc] initWithData:responseObject encoding: 64 65 NSUTF8StringEncoding] 66 67 delegate:self cancelButtonTitle:@”确定” otherButtonTitles:nil] 68 69 show]; 70 71 } 72 73 // 获取服务器响应失败时激发的代码块 74 75 failure:^(AFHTTPRequestOperation *operation, NSError *error) 76 77 { 78 79 NSLog(@” 获取服务器响应出错! ”); 80 81 }]; 82 } 85 } - (IBAction)finishEdit:(id)sender { [sender resignFirstResponder]; } @end /* 上面的红色字代码使用应用程序委托的manager属性(AFHTTPRequestOperationManager对象)的POST方法来发送POST请求, 并使用UIAlertView显示服务器响应. 如果用户名、密码正确,即可看到登录成功的提示. 登录成功后,AFHTTPRequestOperationManager将会自动维护与服务器之间的连接,并维护与服务器之间的Session状态, 再次单击“访问页面”按钮。 使用AFHTTPRequestOperationManager发送GET请求即可正常访问被保护资源,这就是因为前面使用了 AFHTTPRequestOperationManager登录系统, 而且AFHTTPRequestOperationManager可以维护与服务器之间的Session连接。 */ |
处理JSON或Plist响应
AFHTTPRequestOperationManager默认就可以处理JSON或Plist响应。服务器响应数据的MIME(多用途因特网邮件扩展(Multipurpose Internet Mail Extensions))类型是application/json、text/json(这两个代表JSON响应)、
application/x-plist(Plist响应)其中之一时,AFHTTPRequestOperationManager默认就可以处理,
无须指定服务器响应解析器。
当服务器响应数据是JSON或Plist数据,并且AFHTTPRequestOperationManager成功解析得到服务器响应时,
服务器响应数据以NSArray或NSDictionary形式返回.
/* 第1个UITableViewController对应的视图控制器类为AuthorsController,该视图控制器类将会向服务器发送GET请求, 服务器响应返回JSON格式数据.该视图控制器将把JSON响应转换为NSArray集合,并使用UITableView显示该NSArray集合数据. 下面是该视图控制器类的实现部分代码. */ |
AuthorsController.m @interface AuthorsController() { NSArray* authors; AppDelegate* appDelegate; } @end @implementation AuthorsController - (void)viewDidLoad { [super viewDidLoad]; appDelegate = [UIApplication sharedApplication].delegate; NSString* url = @”http://192.168.1.88:8888/AFNetworkingServer/authors.json”; // 使用AFHTTPRequestOperationManager发送GET请求 [appDelegate.manager GET:url parameters:nil //获取服务器响应成功时激发的代码块 success:^(AFHTTPRequestOperation* operation, id responseObject) { // 将服务器响应的JSON数据转换为Objective-C对象,赋值给authors属性 authors = responseObject; // 重新加载表格数据 [self.tableView reloadData]; } // 获取服务器响应失败时激发的代码块 failure:^(AFHTTPRequestOperation *operation , NSError *error) { NSLog(@” 获取作者信息出现错误: %@ ”, error); } ]; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { UITableViewCell* cell = (UITableViewCell*)sender; // 获取激发跳转的单元格所在的NSIndexPath NSIndexPath* indexPath = [self.tableView indexPathForCell:cell]; // 获取即将跳转的目标视图控制器 BooksController* booksController = (BooksController*)segue.destinationViewController; // 将用户选中的单元格的作者信息传给目标视图控制器 booksControlle.selectedAuthor = [authors objectAtIndex:indexPath.row]; // ① } // 省略UITableViewDataSources协议中的两个方法 ….. @end /* 上面的红色字代码发送GET请求时没有指定请求参数,当服务器响应是JSON数据或Plist数据时,服务器响应将被解析成NSArray或NSDictionary对象 ---------这取决于服务器响应数据. */ /* 显示图书列表的视图控制器类将会向服务器发送POST请求,并将作者ID作为参数发送给服务器,从而获取指定作者对应的图书列表. 显示图书列表的视图控制器类的实现部分代码 */ BooksController.m @interface BooksController() { NSArray* books; AppDelegate* appDelegate; } @end @implementation BooksController - (void)viewDidLoad { [super viewDidLoad]; appDelegate = [UIApplication sharedApplication ].delegate; NSString* url = @”http://192.168.1.88:8888/AFNetworkingServer/books.json”; self.navigationItem.title = [NSString stringWithFormat:@”%@的图书”, [self.selectedAuthor objectForKey:@”name”]]; // 使用NSDictionary封装请求参数 NSDictionary* parameters = @{@”authorId”: [self.selectedAuthor objectForKey:@”id”]}; // 使用AFHTTPRequestOperationManager发送GET请求 [ appDelegate.manager POST:url parameters:parameters // 获取服务器响应成功时激发的代码块 success:^(AFHTTPRequestOperation* operation, id responseObject) { // 将服务器响应的JSON数据转换为Objective-C对象,赋值给books属性 books = responseObject; // 重新加载表格数据 [self.tableView reloadData]; } // 获取服务器响应失败时激发的代码块 failure:^(AFHTTPRequestOperation *operation, NSError * error) { NSLog(@” 获取图书信息初心错误: %@”, error); }]; } // 省略UITableViewDataSources协议中的两个方法 …… @end /* 上面的红色字代码发送POST请求时指定了请求参数:将作者ID作为参数发送到服务器,当服务器响应是JSON数据或Plist数据时, 服务器响应将被解析成NSArray或NSDictionary对象--------这取决于服务器响应的数据。 */ |
服务器使用JSON格式的数据,主要基于如下两个理由 |
JSON格式是一种跨语言(几乎所有语言都支持JSON格式)的数据交换格式,而Plist的影响面就小多了。 |
Plist格式的本质依然是XML,因此Plist格式的数据在网络上的传输量更大。 |
处理XML响应
当服务器响应是XML数据时,AFNetworking必须正常处理服务器响应的XML数据,此时则需要为AFHTTPRequestOperationManager显示指定使用AFXMLParserResponseSerializer响应解析器,当使用AFXMLParserResponseSerializer解析器时,服务器返回的数据是一个NSXMLParser对象-------可通过该对象来解析得到服务器响应的数据.
当服务器响应是XML数据时,使用AFNetworking发送请求后得到的响应是NSXMLParser对象,因此此时必须为NSXMLParser指定delegate来解析服务
器响应的XML数据.
@interface AuthorsController () <NSXMLParserDelegate> { AppDelegate* appDelegate; NSMutableArray* authors; NSMutableDictionary* author; NSString* currentTagValue; } @end @implementation AuthorsController - (void)viewDidLoad { [super viewDidLoad]; appDelegate = [UIApplication sharedApplication].delegate; 26 27 NSString* url = @”http://192.168.1.88:8888/AFNetworkingServer/authors.xml”; 28 29 // 使用AFHTTPRequestOperationManager发送GET请求 ① 30 31 [appDelegate.manager GET:url parameters:nil 32 33 // 获取服务器响应成功时激发的代码块 34 35 success:^(AFHTTPRequestOperation* operation, id responseObject) 36 37 { 38 39 // 获取服务器响应的XML数据,并使用NSXMLParser解析该XML数据 40 41 NSXMLParser* parser = responseObject; 42 43 // 指定解析器的delegate是该控制器本身 44 45 parser.delegate = self; 46 47 // 解析服务器响应的XML数据 48 49 [parser parse]; 50 51 // 重新加载表格数据 52 53 [self.tableView reloadData]; 54 55 } 56 57 // 获取服务器响应失败时激发的代码块 58 59 failure:^(AFHTTPRequestOperation* operation, NSError* error) 60 61 { 62 63 NSLog(@” 获取作者信息出现错误: %@” , error); 64 65 }]; 66 67 } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { UITableViewCell* cell = (UITableViewCell*)sender; // 获取激发跳转的单元格所在的NSIndexPath NSIndexPath* indexPath = [self.tableView indexPathForCell:cell]; // 获取即将跳转到的目标视图控制器 BooksController* booksController = (BooksController*)segue.destinationViewController; // 将用户选中的单元格的作者信息传给目标视图控制器 booksController.selecterAuthor = [authors objectAtIndexPath.row]; } // 省略UITableViewDataSource协议中的两个方法 …… // 当开始处理某个元素时触发该方法 - (void)parser: (NSXMLParser*)parser didStartElement:(NSString*)elementName 96 97 namespaceURI:(NSString *)namespaceURI qualifiedName: (NSString*)qName 98 99 attributes: (NSDictionary*)attributeDict // ② 100 { if([elementName isEqualToString:@”authors”]) { // 如果正在处理根元素,在此处初始化存储解析结果的NSMutableArray集合 authors = [[NSMutableArray alloc] init]; } // 如果正在处理<author…/>元素 else if([elementName isEqualToString:@”author”]) { // 初始化NSMutableDictionary对象 author = [[NSMutableDictionary alloc] init]; } } 127 // 当开始处理字符串内容时触发该方法 128 129 // ③ 130 131 - (void)parser:(NSXMLParser*)parser foundCharacters: (NSString*)string { // 如果当前的字符串值不为nil,则保存当前正在处理的元素的值 if(string) { currentTagValue = string; } } // 当处理某个元素结束时触发该方法 - (void)parser: (NSXMLParser*)parser didEndElement: (NSString*)elementName namespaceURI: (NSString*)namespaceURI qualifiedName: (NSString*)qName { // 如果处理根元素结束,则表明XML文档处理完成 if([elementName isEqualToString:@”authors”]) { return; } // 如果处理<author…/>元素结束,则将封装的NSDictionary对象添加到NSMutableArray集合中 else if([elementName isEqualToString:@”author”]) { [author addObject:author]; author = nil; } else { // 如果既不是处理<authors…/>元素,也不是处理<author…/>元素 // 使用KVC方式为当前NSDictionary对象添加key-value对 [author setValue:currentTagValue forKey: elementName]; currentTagValue = nil; } } @end |
从上面的红色字代码不难看出,当服务器响应时XML数据时,AFNetworking发送GET请求、POST请求得到的都是NSXMLParser,程序可通过该对象来解析服务器响应的XML数据------解析服务器数据时,需要自己实现NSXMLParserDelegate协议中的方法来获取服务器响应的XML数据,这样导致编程很麻烦。上面程序中的红色字代码将NSXMLParser的delegate指定为该协议中特定的方法-----------上面程序中的最后3个红色字方法就是为解析XML数据准备的. |
上传文件
使用AFNetworking上传文件直接调用AFHTTPRequestOperationManager的POST:parameters:constructingBodyWithBlock:success:failure:方法提交POST请求即可,该方法比发送普通POST请求的方法多了第3个参数,该参数是一个带AFMultipartFormData
形参的代码块-----可以在该代码块中通过AFMultipartFormData来封装要上传的文件。
AFMultipartFormData提供了如下常用方法: | |
- appendPartWithFileURL:name:error: | 将指定NSURL对应文件封装成上传文件 |
- appendPartWithFileURL:name:fileName:mimeType:error: | 将指定NSURL对应文件封装成上传文件.该方法可指定更多选项 |
- appendPartWithInputStream:name:fileName:length:mimeType: | 将指定输入流中的数据封装成上传文件.该方法可指定更多选项. |
- appendPartWithFileData:name:fileName:mimeType: | 将指定NSData代表的数据封装成上传文件.该方法可指定更多选项. |
代码片段 | |
/* 下面程序使用一个UIPickerView来显示用户想要上传的文件,当用户选中某个文件并单击”上传”按钮后, 程序将使用AFHTTPRequestOperationManager发送上传文件的POST请求,从而将图片上传远程服务器(同样需要服 务器有对应的程序来处理文件上传的请求)。 */ ViewController.m @interface ViewController() { AppDelegate* appDelegate; NSArray* images; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; appDelegate = [UIApplication sharedApplication].delegate; self.picker.dataSource = self; self.picker.delegate = self; // 使用简化语法创建NSArray集合 images = @[@”logo”, @”java”, @”android”]; } // UIPickerViewDataSource中定义的方法,该方法返回值决定该控件包含多少列 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView { // 返回1表明该控件只包含1列 return 1; } - (NSInteger)pickerView: (UIPickerView *)pickerView numberOfRowsInComponent: (NSInteger)component { return images.count; } #define kImageTag 1 - (UIView *)pickerView: (UIPickerView *)pickerView viewForRow: (NSInteger)row forComponent: (NSInteger)component reusingView: (UIView*)view { // 如果可重用的view的tag不等于kImageTag,则表明该View已经不存在,需要重新创建 if(view.tag != kImageTag) { view = [[UIView alloc] init]; // 为该UIView设置tag属性 view.tag = kImageTag; // 设置不允许用户交互 view.userInteractionEnabled = NO; UIImageView* iv = [[UIImageView alloc] initWithImage: [UIImage imageNamed:[images objectAtIndex:row ] ]]; iv.frame = CGRectMake(20, 20 , 48, 48); iv.contentMode = UIViewContentModeScaleAspectFit; [view addSubview:iv]; } return view; } // 省略UIPickeViewDataSource\UIPickerViewDelegate协议中的方法 …… - (IBAction)upload: (id)sender { // 获取用户选中的行 NSInteger selectedRow = [self.picker selectedRowInComponent: 0]; // 获取用户选中的文件名 NSString* filename = [images objectAtIndex: selectedRow]; // 根据用户选中的文件名确定需要上传的文件 NSURL * filePath = [ [NSBundle mainBundle] URLForResource: filename withExtension: @”png”]; NSDictionary* parameters = @{@”name”: @”额外的请求参数”}; // 使用AFHTTPRequestOperationManager 发送POST请求 [appDelegate.manager 128 129 POST:@http://192.168.1.88:8888/AFNetworkingServer/upload 130 131 parameters: parameters 132 133 // 使用代码来封装要上传的文件数据 134 135 constructingBodyWithBlock: ^(id<AFMultipartFormData> formData) 136 137 { 138 139 [formData appendPartWithFileURL:filePath // 指定上传文件 140 141 name: @”file” // 指定上传文件对应的请求参数名 142 143 // 指定上传文件的原始文件名 144 145 fileName: [NSString stringWithFormat : @”%@.png “, fileName ] 146 147 // 指定上传文件的MIME类型 148 149 mimeType : @”image/png” 150 151 error: nil ]; 152 153 } 154 155 // 获取服务器响应成功时激发的代码块 156 157 success : ^(AFHTTPRequestOperation* operation, id responseObject) 158 159 { 160 161 // 当使用HTTP响应解析器时, 服务器响应数据被封装在NSData中 162 163 // 此处将NSData转换成NSString, 并使用UIAlertView显示登录结果 164 165 [[[UIAlert alloc] initWithTitle: @” 登录结果 ” message: 166 167 [[NSString alloc] initWithData:responseObject encoding: 168 169 NSUTF8StringEncoding ] delegate: self 170 171 cancelButtonTitle:@” 确定 ” otherButtonTitles: nil ] 172 173 show ]; 174 175 } 176 177 // 获取服务器响应失败时激发的代码块 178 179 failure: ^(AFHTTPRequestOperation* operation, NSError* error) 180 181 { 182 183 NSLog(@” 获取服务器响应出错! ”); 184 185 }]; 186 187 } @end | |
该程序的关键在于红色字代码块,该代码块位于AFHTTPRequestOperationManager发送POST请求的方法中,该代码块使用AFMultipartFormData的方法 把用户选中的文件封装成上传文件,随着POST请求提交给远程服务器-----远程服务器会处理用户上传的文件. |
相关文章推荐
- HTTP协议状态码详解(HTTP Status Code)
- 关于Xcode 7.2版本更新后需要注意的东西
- 外贸市场越来越淡,个人soho,小外贸B2C公司如何转型? Read more: http://liedaoshou.com/seo.html#0-sqq-1-60778-9737f6f9e09df
- apt-get彻底卸载删除软件http://www.tennfy.com/1518.html
- HTTP Cookie 详解
- 神经网络实现【转载】
- TCP/IP协议
- 谈一谈网络编程学习经验(06-08更新)
- Android异步加载网络图片
- 计算机网络学习资源
- python001环境搭建及入门 http://python.jobbole.com/81332/
- 【干货篇】调用其他系统http接口超时了,如何处理,方案汇总
- httplib下载http链接的DOC文档
- linux socket编程之TCP与UDP
- HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalArgumentException:
- Linux下以C构建WEB服务并响应XHR(XMLHttpRequest)请求
- 《TCP/IP详解 卷1:协议》 读书笔记 第十九章 TCP的交互数据流
- HTTP POST GET SOAP本质区别详解
- cURL进行HTTPS连接POST数据
- ios 网络请求后 Crash