您的位置:首页 > 理论基础 > 计算机网络

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请求提交给远程服务器-----远程服务器会处理用户上传的文件.

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: