ASIHttpRequest是一个很好的库,不过直接使用稍嫌麻烦,下面就尝试来封装一下吧!
思路:每次请求时,需要创建一个ASIHttpRequest对象,设置它的属性(url,delegate,postValue,requestMethod等)之后即可开始异步请求。所以我们可以创建一个类,对外提供一个请求方法,结构如下:
@interface RequestService : NSObject + (void)requestWithUrlString:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod delegate:(id)delegate; @end
使用如下:
[RequestService requestWithUrlString:urlString params:params httpMethod:httpMethod delegate:delegate];
看起来很简单嘛,这样就行了吗?
考虑一个问题,此时传入的delegate是请发起请求的对象,如果我们想要对请求返回做统一的处理,就无法做到了。为了实现这一目的,可以将ASIHttpRequest的delegate设置成RequestService实例对象,请求返回昨晚处理后再将处理结果通知给发起请求的对象。那么RequestService请求的方法就需要改成实例方法。
修改后代码如下:
@protocol RequestServiceDelegate <NSObject> - (void)requestWillBegin; - (void)requestDidSucceedWithData:(id)data; - (void)requestDidFailWithError:(NSError *)error; @end @interface RequestService : NSObject @property (nonatomic,assign)id<RequestServiceDelegate> delegate; - (void)requestWithUrlString:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod; @end
注意除了将类方法修改成实例方法之外,我们去掉了方法传参ASIHttpRequestDelegate,同时给它加了自己的delegate属性,用于在请求返回处理完成后通知发起请求的对象。
使用如下:
RequestService *request = [[RequestService alloc] init]; request.delegate = self; [request requestWithUrlString:urlString params:params httpMethod:httpMethod];
在这里需要特别注意创建的request对象的生命周期,因为ASIHttpRequest执行的是异步请求,当请求完成,通知request对象时,如果request对象已经销毁,那么代码就会报错。因此使用时,需要发起请求的对象持有此request,并在请求完成后释放它。
让每个对象管理请求,听起来就很麻烦,又违背了想要统一管理的初衷,那么应该怎么办呢?
让我们来使用单例吧!
创建一个单例对象HttpClient,它有一个属性requestItems。每次发起请求时,将请求的Item存入该属性。请求结束后移除Item。对外,它要提供一个request方法,并需要一个属性保存发起请求的对象。
HttpClient的结构如下:
@protocol HttpClientDelegate <NSObject> - (void)requestDidFinishWithData:(NSDictionary *)data urlString:(NSString *)urlString nTag:(NSInteger)nTag sKey:(NSString *)sKey; - (void)requestDidFailWithError:(NSError *)error urlString:(NSString *)urlString nTag:(NSInteger)nTag sKey:(NSString *)sKey; @end @interface HttpClient : NSObject @property (nonatomic,strong)NSMutableDictionary *items; + (HttpClient *)sharedClient; - (void)requestWithUrl:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod delegate:(id<HttpClientDelegate>)delegate; @end
使用如下:
[[HttpClient sharedClient] requestWithUrl:kProductListURL params:params httpMethod:@"GET" delegate:self];
HttpClient内部如何实现对每次request的管理呢?首先在请求时,创建一个requestItem,调用requestItem的请求方法,然后将其加入requestItems。当requestItem接收到返回时,通知HttpClient,最后由HttpClient通知发起请求的对象,并移除该requestItem。
HttpClient的request方法如下:
- (void)requestWithUrl:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod delegate:(id<HttpClientDelegate>)delegate { HttpRequestItem *item = [[HttpRequestItem alloc] init]; item.delegate = delegate; [item requestWithUrlString:urlString params:params httpMethod:httpMethod]; NSUInteger hashValue = [item hash]; [self.items addObject:item]; }
用于接收RequestItem的通知方法如下:
- (void)didFinishWithItem:(HttpRequestItem *)item error:(NSError *)error { if (error == nil) { // 请求成功 if (item.delegate && [item.delegate respondsToSelector:@selector(requestDidFinishWithData:urlString:nTag:sKey:)]) { NSDictionary *data = [NSJSONSerialization JSONObjectWithData:item.httpReq.responseData options:NSJSONReadingMutableContainers error:nil]; [item.delegate requestDidFinishWithData:data urlString:item.urlString nTag:item.nTag sKey:item.sKey]; } } else { // 请求出错 if (item.delegate && [item.delegate respondsToSelector:@selector(requestDidFailWithError:urlString:nTag:sKey:)]) { [item.delegate requestDidFailWithError:error urlString:item.urlString nTag:item.nTag sKey:item.sKey]; } } [self removeItem:item]; }
HttpRequestItem也就是我们之前的RequestService,它接收ASIHttpRequest的返回的方法如下:
#pragma mark - ASIHTTPRequest delegate - (void)requestFinished:(ASIHTTPRequest *)request { NSError *error = nil; // doSomething with response here.... if ([[HttpClient sharedClient] respondsToSelector:@selector(didFinishWithItem:error:)]) { [[HttpClient sharedClient] performSelector:@selector(didFinishWithItem:error:) withObject:self withObject:error]; } } - (void)requestFailed:(ASIHTTPRequest *)request { if ([[HttpClient sharedClient] respondsToSelector:@selector(didFinishWithItem:error:)]) { [[HttpClient sharedClient] performSelector:@selector(didFinishWithItem:error:) withObject:self withObject:request.error]; } }
以上,基本的雏形已经出来了~还有哪些可以改进的地方呢?
考虑两个问题:
1.同一个对象,可能会发起多次请求,如何区别这些请求的返回?
2.如果想主动取消某个正在进行的请求,应该怎么办?
对于问题1,可在请求时传入参数,如tag、key等,由RequestItem保存这些参数,请求完成后根据这些参数来区分不同的请求。
对于问题2,对应方法是记录每个RequestItem的HashValue,并对外提供根据HashValue取消对应请求的方法。
完整的代码如下:
HttpClient.h
#import <Foundation/Foundation.h> @protocol HttpClientDelegate <NSObject> - (void)requestDidFinishWithData:(NSDictionary *)data urlString:(NSString *)urlString nTag:(NSInteger)nTag sKey:(NSString *)sKey; - (void)requestDidFailWithError:(NSError *)error urlString:(NSString *)urlString nTag:(NSInteger)nTag sKey:(NSString *)sKey; @end @interface HttpClient : NSObject @property (nonatomic,strong)NSMutableDictionary *items; + (HttpClient *)sharedClient; - (NSUInteger)requestWithUrl:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod delegate:(id<HttpClientDelegate>)delegate; - (NSUInteger)requestWithUrl:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod delegate:(id<HttpClientDelegate>)delegate nTag:(NSInteger)nTag; - (NSUInteger)requestWithUrl:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod delegate:(id<HttpClientDelegate>)delegate sKey:(NSString *)sKey; - (NSUInteger)requestWithUrl:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod delegate:(id<HttpClientDelegate>)delegate nTag:(NSInteger)nTag sKey:(NSString *)sKey; // 取消请求 - (BOOL)cancelRequestWithHashValue:(NSUInteger)hashValue; @end
HttpClient.m
#import "HttpClient.h" #import "HttpRequestItem.h" @implementation HttpClient + (HttpClient *)sharedClient { static HttpClient *_sharedClient = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _sharedClient = [[self alloc] init]; _sharedClient.items = [NSMutableDictionary dictionary]; }); return _sharedClient; } - (NSUInteger)requestWithUrl:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod delegate:(id<HttpClientDelegate>)delegate { return [self requestWithUrl:urlString params:params httpMethod:httpMethod delegate:delegate nTag:0 sKey:nil]; } - (NSUInteger)requestWithUrl:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod delegate:(id<HttpClientDelegate>)delegate nTag:(NSInteger)nTag { return [self requestWithUrl:urlString params:params httpMethod:httpMethod delegate:delegate nTag:nTag sKey:nil]; } - (NSUInteger)requestWithUrl:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod delegate:(id<HttpClientDelegate>)delegate sKey:(NSString *)sKey { return [self requestWithUrl:urlString params:params httpMethod:httpMethod delegate:delegate nTag:0 sKey:sKey]; } - (NSUInteger)requestWithUrl:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod delegate:(id<HttpClientDelegate>)delegate nTag:(NSInteger)nTag sKey:(NSString *)sKey { HttpRequestItem *item = [[HttpRequestItem alloc] init]; item.delegate = delegate; item.nTag = nTag; item.sKey = sKey; [item requestWithUrlString:urlString params:params httpMethod:httpMethod]; NSUInteger hashValue = [item hash]; [self.items setObject:item forKey:@(hashValue)]; return hashValue; } - (BOOL)cancelRequestWithHashValue:(NSUInteger)hashValue { HttpRequestItem *item = [_items objectForKey:@(hashValue)]; if (item) { [item.httpReq clearDelegatesAndCancel]; [_items removeObjectForKey:@(hashValue)]; } return YES; } #pragma mark - actions - (void)removeItem:(HttpRequestItem *)item { NSUInteger hashValue = [item hash]; id object = [_items objectForKey:@(hashValue)]; if (object != nil) { [[(HttpRequestItem *)object httpReq] clearDelegatesAndCancel]; [_items removeObjectForKey:@(hashValue)]; } } #pragma mark - HttpRequestItem delegate // 请求结束 - (void)didFinishWithItem:(HttpRequestItem *)item error:(NSError *)error { if (error == nil) { // 请求成功 if (item.delegate && [item.delegate respondsToSelector:@selector(requestDidFinishWithData:urlString:nTag:sKey:)]) { NSDictionary *data = [NSJSONSerialization JSONObjectWithData:item.httpReq.responseData options:NSJSONReadingMutableContainers error:nil]; [item.delegate requestDidFinishWithData:data urlString:item.urlString nTag:item.nTag sKey:item.sKey]; } } else { // 请求出错 if (item.delegate && [item.delegate respondsToSelector:@selector(requestDidFailWithError:urlString:nTag:sKey:)]) { [item.delegate requestDidFailWithError:error urlString:item.urlString nTag:item.nTag sKey:item.sKey]; } } [self removeItem:item]; } @end
HttpRequestItem.h
#import <Foundation/Foundation.h> #import "ASIFormDataRequest.h" #import "RequestConstants.h" #import "HttpClient.h" @interface HttpRequestItem : NSObject @property (nonatomic,strong)ASIHTTPRequest *httpReq; @property (nonatomic,copy)NSString *urlString; @property (nonatomic,assign)NSInteger nTag; @property (nonatomic,copy)NSString *sKey; @property (nonatomic,assign)id<HttpClientDelegate> delegate; - (void)requestWithUrlString:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod; @end
HttpRequestItem.m
#import "HttpRequestItem.h" NSString * const kHttpBaseURLString = @"http://192.168.33.6:8080/"; @implementation HttpRequestItem - (void)requestWithUrlString:(NSString *)urlString params:(NSDictionary *)params httpMethod:(NSString *)httpMethod { ASIFormDataRequest *request; NSURL *url; urlString = [kHttpBaseURLString stringByAppendingString:urlString]; if ([[httpMethod lowercaseString] isEqualToString:@"post"]) { // post方式访问接口 url = [NSURL URLWithString:urlString]; request = [ASIFormDataRequest requestWithURL:url]; [request setRequestMethod:@"POST"]; for (NSString *key in params) { id value = [params objectForKey:key]; if (![value isKindOfClass:[NSData class]]) { [request addPostValue:value forKey:key]; } else { [request addData:value forKey:key]; } } } else { // get方式访问接口 urlString = [urlString stringByAppendingString:@"?format=json"]; for (NSString *key in params) { urlString = [urlString stringByAppendingFormat:@"&%@=%@", key, [params objectForKey:key]]; } NSLog(@"-----------url:%@-----------",urlString); NSURL *url = [NSURL URLWithString:urlString]; request = [ASIFormDataRequest requestWithURL:url]; //设置缓存方式 //[request setSecondsToCache:60*60*24*30]; //[request setDownloadCache:[DFAppDelegate myCache]]; //设置缓存数据存储策略,这里采取的是如果无更新或无法联网就读取缓存数据 [request setCachePolicy:ASIAskServerIfModifiedCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy]; [request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy]; [request setRequestMethod:@"GET"]; } request.delegate = self; request.timeOutSeconds = 60; [request startAsynchronous]; self.httpReq = request; self.urlString = urlString; } #pragma mark - ASIHTTPRequest delegate - (void)requestFinished:(ASIHTTPRequest *)request { NSError *error = nil; if (request.responseStatusCode == 500) { // 服务器状态500 error = [NSError errorWithDomain:@"服务器出错" code:-10000 userInfo:nil]; } id result = [NSJSONSerialization JSONObjectWithData:request.responseData options:NSJSONReadingMutableContainers error:nil]; if ([result isKindOfClass:[NSDictionary class]]) { NSString *status = [result objectForKey:@"status"]; if (![status isEqualToString:@"success"]) { // 请求返回errorCode error = [NSError errorWithDomain:[result objectForKey:@"info"] code:-9998 userInfo:nil]; } } else { error = [NSError errorWithDomain:@"服务器返回值出错" code:-9999 userInfo:nil]; } if ([[HttpClient sharedClient] respondsToSelector:@selector(didFinishWithItem:error:)]) { [[HttpClient sharedClient] performSelector:@selector(didFinishWithItem:error:) withObject:self withObject:error]; } } - (void)requestFailed:(ASIHTTPRequest *)request { if ([[HttpClient sharedClient] respondsToSelector:@selector(didFinishWithItem:error:)]) { [[HttpClient sharedClient] performSelector:@selector(didFinishWithItem:error:) withObject:self withObject:request.error]; } } @end