CBNetworking AFN 的封装,别出心裁

iOS · cbangchen · 于 发布 · 704 次阅读
96

为什么要对AFNetworking进行多一层的封装

对于AFNetworking进行二次封装是很有必要的。事实上,在项目中大量使用第三方网络库是有风险的,因为网络请求的使用遍布整个应用,而一旦该对应的网络库不再更新维护,或者我们有意愿的去更换网络框架,修改将会有着巨大的难以承受的工作量。

这一个框架与其他框架有什么不同

  • 本框架基于AFNetworking3.0的版本进行封装,面向更新的版本。

  • 为网络请求的任务管理做了大量的工作,使得下载上传,或者其他使用环境下的任务管理得以更轻松的实现。

  • 反其道而行,专为Post请求作出了缓存处理,这一部分缓存处理的使用与否,由使用者自行决定。

  • 自定义Get请求的缓存策略,以时间为基准,严格把控缓存的有效性。

接口设计

请求

这里使用了新建NS_ENUM的方式来统一Post和Get接口。

/**
 *  请求方式
 */
typedef NS_ENUM(NSInteger, CBRequestType) {
    /**
     *  POST方式来进行请求
     */
    CBPOSTRequest = 1 << 0,
    /**
     *  GET方式来进行请求
     */
    CBGETRequest  = 1 << 1
};
/**
 *  统一请求接口
 *
 *  @param url              请求路径
 *  @param params           拼接参数
 *  @param httpMethod       请求方式(0为POST,1为GET)
 *  @param useCache         是否使用缓存
 *  @param progressBlock    进度回调
 *  @param successBlock     成功回调block
 *  @param failBlock        失败回调block
 *
 *  @return 返回的对象中可取消请求
 */
+ (CBURLSessionTask *)requestWithUrl:(NSString *)url
                              params:(NSDictionary *)params
                            useCache:(BOOL)useCache
                         httpMedthod:(CBRequestType)httpMethod
                       progressBlock:(CBNetWorkingProgress)progressBlock
                        successBlock:(CBResponseSuccessBlock)successBlock
                           failBlock:(CBResponseFailBlock)failBlock;

从上面的代码我们可以看到,参数useCache决定着你是否使用Post请求的缓存功能,而即便将其设置为NO,依然会自动开启Get请求的缓存功能。

数据解析方式

/**
 *  数据串行方式
 */
typedef NS_ENUM(NSInteger, CBSerializerType) {
    /**
     *  HTTP方式来进行请求或响应
     */
    CBHTTPSerializer = 1 << 0,
    /**
     *  JSON方式来进行请求或响应
     */
    CBJSONSerializer = 1 << 1
};
/**
 *  更新请求或者返回数据的解析方式(0为HTTP模式,1为JSON模式)
 *
 *  @param requestType  请求数据解析方式
 *  @param responseType 返回数据解析方式
 */
+ (void)updateRequestSerializerType:(CBSerializerType)requestType
                 responseSerializer:(CBSerializerType)responseType;

上面的NS_ENUM联合上面的方法可以更改数据解析方式,更具灵活性。

文件上传,下载

/**
 *  文件上传接口
 *
 *  @param url              传文件接口地址
 *  @param uploadingFile    上传文件路径
 *  @param progressBlock    上传进度
 *  @param successBlock     成功回调
 *  @param failBlock        失败回调
 *
 *  @return 返回的对象中可取消请求
 */
+ (CBURLSessionTask *)uploadFileWithUrl:(NSString *)url
                          uploadingFile:(NSString *)uploadingFile
                          progressBlock:(CBNetWorkingProgress)progressBlock
                           successBlock:(CBResponseSuccessBlock)successBlock
                              failBlock:(CBResponseFailBlock)failBlock;
/**
 *  文件下载接口
 *
 *  @param url           下载文件接口地址
 *  @param saveToPath    存储目录
 *  @param progressBlock 下载进度
 *  @param successBlock  成功回调
 *  @param failBlock     下载回调
 *
 *  @return 返回的对象可取消请求
 */
+ (CBURLSessionTask *)downloadWithUrl:(NSString *)url
                           saveToPath:(NSString *)saveToPath
                        progressBlock:(CBNetWorkingProgress)progressBlock
                         successBlock:(CBResponseSuccessBlock)successBlock
                            failBlock:(CBResponseFailBlock)failBlock;

针对文件的下载和上传的做出专门的设计。

任务管理

由上面的方法中,我们可以看到每一个方法都返回了一个任务对象CBURLSessionTask,这个对象继承于NSURLSessionTask,如此我们可以直接的取到每次进行任务请求的对象,直接对其进行管理。但不止于此,我仍然写了以下的几个接口来更快捷更集中的对其进行管理。

/**
 *  取消所有请求
 */
+ (void)cancelAllRequest;
/**
 *  根据url取消请求
 *
 *  @param url 请求url
 */
+ (void)cancelRequestWithURL:(NSString *)url;

通过这两个方法,我们可以直接取消所有的请求,或者取消单个链接对应的请求。全局性的执行取消操作,更方便。

缓存处理

Post缓存处理

由于苹果官方的NSURLCache不支持Post请求的缓存处理,所有这一部分的缓存处理,我自己通过归档的方式来进行管理。

主要的方法如下:

+ (id)getCacheResponseWithURL:(NSString *)url
                       params:(NSDictionary *)params {
    id cacheData = nil;

    if (url) {
        NSString *directoryPath = DIRECTORYPATH;

        NSString *originString = [NSString stringWithFormat:@"%@+%@",url,params];

        NSString *path = [directoryPath stringByAppendingPathComponent:[self md5:originString]];
        NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
        if (data) {
            cacheData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
        }
    }
    return cacheData;
}
+ (void)cacheResponseObject:(id)responseObject
                    request:(NSURLRequest *)request
                     params:(NSDictionary *)params {
    if (request && responseObject && ![responseObject isKindOfClass:[NSNull class]]) {
        NSString *directoryPath = DIRECTORYPATH;

        NSError *error = nil;
        if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
            [[NSFileManager defaultManager] createDirectoryAtPath:directoryPath
                                      withIntermediateDirectories:YES
                                                       attributes:nil
                                                            error:&error];
        }

        NSString *originString = [NSString stringWithFormat:@"%@+%@",request.URL.absoluteString,params];

        NSString *path = [directoryPath stringByAppendingPathComponent:[self md5:originString]];
        NSDictionary *dict = (NSDictionary *)responseObject;

        NSData *data = nil;
        if ([dict isKindOfClass:[NSData class]]) {
            data = responseObject;
        } else {
            data = [NSJSONSerialization dataWithJSONObject:dict
                                                   options:NSJSONWritingPrettyPrinted
                                                     error:&error];
        }
        if (data && error == nil) {
            [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
        }
    }
}

+ (NSString *)md5:(NSString *)string {
    if (string == nil || [string length] == 0) {
        return nil;
    }
    unsigned char digest[CC_MD5_DIGEST_LENGTH], i;
    CC_MD5([string UTF8String], (int)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding], digest);
    NSMutableString *ms = [NSMutableString string];

    for (i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
        [ms appendFormat:@"%02x", (int)(digest[i])];
    }

    return [ms copy];
}

值得注意的是,每一次进行缓存,都通过HASH的方式对缓存进行了加密,从而达到唯一缓存和更加安全的目的。

Get请求的自定义

按道理,官方已经对于Get请求进行了很完善的缓存处理,为什么我还要自行处理呢?事实上,我并没有自定义这一部分的处理,我仅对于NSURLCache加多了一层封装,从而达到自定义策略的目的,主要的方法如下所示:

- (id)cachedResponseForRequest:(NSURLRequest *)request {
    NSCachedURLResponse *cachedResponse = [super cachedResponseForRequest:request];

    if (cachedResponse) {
        NSDate *cacheDate = cachedResponse.userInfo[CBURLCacheExpirationKey];

        NSDate *cacheExpirationDate = [cacheDate dateByAddingTimeInterval:CBURLCacheExpirationInterval];

        if ([cacheExpirationDate compare:[NSDate date]] == NSOrderedAscending) {
            [self removeCachedResponseForRequest:request];
            return nil;
        }
    }

    id responseObj = [NSJSONSerialization JSONObjectWithData:cachedResponse.data options:NSJSONReadingAllowFragments error:nil];

    return responseObj;
}
- (void)storeCachedResponse:(id)response
               responseObjc:(id)responseObjc
                 forRequest:(NSURLRequest *)request {
    NSData *data = [NSJSONSerialization dataWithJSONObject:responseObjc options:NSJSONWritingPrettyPrinted error:nil];

    NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];

    userInfo[CBURLCacheExpirationKey] = [NSDate date];

    NSCachedURLResponse *modifiedCachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:userInfo storagePolicy:0];

    [super storeCachedResponse:modifiedCachedResponse forRequest:request];
}

小结

更详细的代码请大家点击下载查看,谢谢大家的关注,如果可以,请点个star支持一下,谢谢。

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册