AFNetworking毫无疑问是目前iOS开发中(Objective-C)最好的第三方网络库。一般规模的app使用AFNetworking就可以完成所有需求,不需要自己造轮子。
在使用AFNetworking的过程中遇到了许多坑,也获得了不少经验,在这里总结一下,希望能够帮助到大家。
AFNetworking进一步封装
AFNetworking 3.x 已经为我们提供了丰富而且易用的API,但还是不够。AFNetworking是一个通用的库,而在我们自己的项目中,有特定的需求,而且往往在许多类中都需要用到网络请求。对AFNetworking进一步封装,能够大大简化代码,减少重复。
举个例子,新建一个CDMNetworkManager类,继承AFHTTPSessionManager。
//CDMNetworkManager.h
#import <AFNetworking/AFNetworking.h>
typedef void(^requestSuccessBlock) (NSURLSessionTask *task, id resposeObject);
typedef void(^requestFailureBlock) (NSURLSessionTask *task, NSError *error);
@interface CDMNetworkManager : AFHTTPSessionManager
+(instancetype)sharedManager;
-(void)sendPostRequest:(NSString *)path parameters:(NSDictionary *)parameters success:(requestSuccessBlock)success failure:(requestFailureBlock)failure;
//CDMNetworkManager.m
#import "CDMNetworkManager.h"
typedef void(^requestSuccessBlock) (NSURLSessionTask *task, id resposeObject);
typedef void(^requestFailureBlock) (NSURLSessionTask *task, NSError *error);
@implementation CDMNetworkManager
+(instancetype)sharedManager{
static CDMNetworkManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc] initWithBaseURL:[NSURL URLWithString:@"http://www.someurl.com/"]];
});
return manager;
}
-(void)sendPostRequest:(NSString *)path parameters:(NSDictionary *)parameters success:(requestSuccessBlock)success failure:(requestFailureBlock)failure{
[self POST:path parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject){
success(task,responseObject);
} failure:^(NSURLSessionTask *task, NSError *error){
failure(task,error);
}];
}
初始化方法中使用了单例模式,这样与服务器的连接不会断掉,省去了每次建立连接时三次握手等过程。如果有多个URL地址,可以创建多个单例。如果有更多特殊的要求,比如设置超时时间等,可以在这个方法中设置。
具体的网络请求方法这里只封装了一个简单的post方法,如果有需要可以封装别的方法。
调用的时候很简单,在任何一个需要发送post请求的类中:
[[CDMNetworkManager sharedManager] sendPostRequest:@"XXX" parameters:parameter success:^(NSURLSessionTask *task, id responseObject){
//your code
} failure:^(NSURLSessionTask *task, NSError *error){
// your code
}];
调试工具
曾经我每次网络请求调试的时候只会在回调里写log,然后在控制台里一行一行的仔细查找。如果这个请求返回的内容很多,在Xcode里的显示效果就很差,而且不能直接显示中文。体验很差!
这时候需要神器Charles
上面是Charles的截图,能够监听Mac的所有网络请求并且显示各种格式的内容,非常好用。如果用模拟器的话就可以直接用Charles抓包,如果真机调试的话,需要设置一下iPhone,网上也有很多教程。
虽然是付费软件,但是不买license只是打开时会有10秒延时,每隔30分钟自动关闭。
常见错误
Error Domain=com.alamofire.error.serialization.response Code=-1016 "Request failed: unacceptable content-type: text/html"
出现这个问题的原因有很多,从表面上看是返回数据的格式不支持text/html,但是为什么返回了text/html?原因不唯一。
首先要看请求的response code,如果是200,说明请求已经传到服务器并且成功返回,返回的格式是text/html。AFNetworking的默认response serializer是AFJSONResponseSerializer,不支持text/html。
这时需要在manager的初始化方法中增加一下代码。或者与服务器开发人员沟通,改写服务器端。
manager.responseSerializer.acceptableContentTypes = [manager.responseSerializer.acceptableContentTypes setByAddingObject:@"text/html"];
如果response code是400,说明服务器根本没有收到请求。这时为什么返回了一个text/html的数据呢?返回的数据在Xcode控制台上显示为16进制,但是在Charles里我们可以看到结果
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>请求错误</title>
<style>BODY { color: #000000; background-color: white; font-family: Verdana; margin-left: 0px; margin-top: 0px; } #content { margin-left: 30px; font-size: .70em; padding-bottom: 2em; } A:link { color: #336699; font-weight: bold; text-decoration: underline; } A:visited { color: #6699cc; font-weight: bold; text-decoration: underline; } A:active { color: #336699; font-weight: bold; text-decoration: underline; } .heading1 { background-color: #003366; border-bottom: #336699 6px solid; color: #ffffff; font-family: Tahoma; font-size: 26px; font-weight: normal;margin: 0em 0em 10px -20px; padding-bottom: 8px; padding-left: 30px;padding-top: 16px;} pre { font-size:small; background-color: #e5e5cc; padding: 5px; font-family: Courier New; margin-top: 0px; border: 1px #f0f0e0 solid; white-space: pre-wrap; white-space: -pre-wrap; word-wrap: break-word; } table { border-collapse: collapse; border-spacing: 0px; font-family: Verdana;} table th { border-right: 2px white solid; border-bottom: 2px white solid; font-weight: bold; background-color: #cecf9c;} table td { border-right: 2px white solid; border-bottom: 2px white solid; background-color: #e5e5cc;}</style>
</head>
<body>
<div id="content">
<p class="heading1">请求错误</p>
<p xmlns="">服务器处理请求时遇到错误。有关构造有效服务请求的内容,请参阅<a rel="help-page" href="http://www.zjubiomedit.com:13264/HypertensionService.svc/help">服务帮助页</a>。</p>
</div>
</body>
</html>
这是自动返回的一个错误页面,而不是服务器返回的数据格式不对。这时候再去找response serializer的问题就南辕北辙了。
那么为什么服务器没有受到请求?
一种可能是baseURL的问题。AFHTTPSessionManager的初始化中用到了baseURL,与之后的path拼接时可能出错,检查一下。
还有可能是request serializer的问题。AFNetworking默认的是AFHTTPRequestSerializer。如果服务器接受的参数只能是json的话,需要使用AFJSONRequestSerializer。
self.requestSerializer = [AFJSONRequestSerializer serializer];
可能还有其他各种各样奇怪的原因,欢迎大家补充。
作者时间、能力有限,文章难免有不足之处,请大家指正。
网友评论