# 接口請求的本地緩存策略前端
本文是在 AFNetworking 的基礎上,加入了本地緩存策略,並對請求錯誤,進行了定製,以統一前端對用戶的錯誤提示。緩存
## 緩存策略服務器
包含3種策略,詳見如下緩存策略的定義說明。網絡
```優化
/**url
接口緩存策略.net
*/orm
typedef NS_ENUM(NSUInteger, TTVRequestCachePolicy) {接口
//一、先查詢本地緩存ip
//2.一、有緩存則直接返回
//2.二、無緩存則請求服務器後返回,並更新本地緩存
TTVRequestCachePolicyNormal,
//不使用緩存,直接請求服務器
TTVRequestCachePolicyOnline,
//一、先查詢本地緩存。
//2.一、有緩存則返回,而後請求服務器再次返回,並更新本地緩存。
//2.二、無緩存則聯網請求服務器返回最新數據,並更新本地緩存。
TTVRequestCachePolicyHybrid,
};
```
### 策略對應的業務需求
##### TTVRequestCachePolicyNormal
只取本地緩存的數據返回。同時在後臺請求最新的數據,並更新緩存。
對應的業務需求是,一些更新週期很是長的數據,能夠使用此種策略。
##### TTVRequestCachePolicyOnline
只請求最新的網絡數據。
對應一些不須要使用到本地緩存的業務需求。
##### TTVRequestCachePolicyHybrid
先返回本地緩存數據,再返回最新的網絡數據。
由於App端有一種通用的需求,就是在沒有網絡或網絡較差時,須要先展現本地緩存的數據,同時請求最新數據,請求成功後,將獲取到的最新數據更新到UI上。
## 請求成功或失敗的回調block
### 請求成功的處理
```
/**
請求的成功回調
@param responseObject 返回的正確數據
@param isCache 是否緩存
*/
typedef void (^TTVRequestSuccessBlock)(id _Nullable responseObject, BOOL isCache);
```
指定的返回的數據,以及 `isCache` 表明是否來自本地緩存
### 請求失敗的處理
```
/**
請求的失敗回調
@param errCode 錯誤碼
@param errMessage 錯誤消息
*/
typedef void (^TTVRequestFailureBlock)(NSInteger errCode, NSString *_Nullable errMessage);
```
請求失敗的回調block,指定了錯誤碼和錯誤提示語,在調用請求接口時,可直接使用並展現給用戶。
在請求過程當中,加入了網絡錯誤的判斷,直接返回指定的網絡錯誤提示語,並中斷請求。
## 有待優化
對於本地緩存策略,須要加入緩存有效期。
## 完整代碼示例
```
/**
GET或者POST請求
@param isGetOrPost YES:GET請求,NO:POST請求
@param URLString 請求的URL
@param parameters 請求參數
@param cachePolicy 緩存策略
@param cacheKey 緩存key,命名方式:模塊名+id+userId
@param downloadProgressBlock 請求成功下載加載進度回調Block
@param successBlock 請求成功調用Block,Block中的responseObject參數
@param failureBlock 請求失敗調用Block
@return
*/
+ (nonnull NSURLSessionDataTask *)request:(BOOL)isGetOrPost
url:(nonnull NSString *)URLString
parameters:(nullable id)parameters
cachePolicy:(TTVRequestCachePolicy)cachePolicy
cacheKey:(NSString *_Nullable)cacheKey
progress:(void (^ _Nullable)(NSProgress * _Nonnull progress))downloadProgressBlock
success:(TTVRequestSuccessBlock)successBlock
failure:(TTVRequestFailureBlock)failureBlock
{
TTVLogVerbose(@"GET Request with Cache>>>>>>>>>> [%@][%@]", URLString, parameters);
AFHTTPSessionManager *manager = [TTVNetworkManager ttv_addHeaderForSignWithRequestURL:URLString mathod:isGetOrPost ? @"GET" : @"POST" parameters:parameters needToDecrypt:YES];
__block YYCache *yyCache = [YYCache cacheWithName:@"TTVRequestCache"];
//緩存key,加上版本號,用於升級接口後的數據更新
__block NSString *cacheKeyFinal = [NSString stringWithFormat:@"%@+%@", cacheKey, [[self class] getServerAPIVesion:URLString]];
//成功回調處理
void (^doSuccessBlock)(id, BOOL) = ^(id responseObject, BOOL isCache) {
TTVLogVerbose(@"GET Request with Cache Success<<<<<<<<<<[ %@ ]\n isCache:%@\n responseObject: %@ \n\n", URLString, @(isCache), responseObject);
if (successBlock) {
successBlock(responseObject, isCache);
if (!isCache) {
[self checkResponseObject:responseObject];
}
}
};
//失敗回調處理
void (^doFailureBlock)(NSError *, TTVRequestFailureBlock) = ^(NSError *error, TTVRequestFailureBlock failureBlock) {
TTVLogWarn(@"GET Request with Cache Failure<<<<<<<<<<[ %@ ]\n%@\n\n", URLString, error);
//返回具體的錯誤提示
if (failureBlock) {
TTVNetWorkError *netWorkError = [TTVNetWorkError ttv_errorWithError:error userInfo:error.userInfo];
NSInteger errorCode = 0;
if (error) {
errorCode = [error.userInfo[@"body"][@"errorCode"] integerValue];
}
failureBlock(errorCode, netWorkError.ttv_tips);
}
};
//請求
void (^requestBlock)(BOOL) = ^(BOOL isGetOrPost)
{
/********************************
無網絡時,直接返回錯誤消息
*/
AFNetworkReachabilityStatus status = [AFNetworkReachabilityManager sharedManager].networkReachabilityStatus;
if (status == AFNetworkReachabilityStatusNotReachable) {
if (failureBlock) {
failureBlock(TTVRequestErrorCodeNoNetwork, TTVErrorMessageNoNetwork);
}
return;
}
/********************************
網絡正常時,繼續請求
*/
if (isGetOrPost) {
[manager GET:URLString
parameters:parameters
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
{
doSuccessBlock(responseObject, NO);
[yyCache setObject:responseObject forKey:cacheKeyFinal];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
{
doFailureBlock(error, failureBlock);
}];
} else {
[manager POST:URLString
parameters:parameters
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
{
doSuccessBlock(responseObject, NO);
[yyCache setObject:responseObject forKey:cacheKeyFinal];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
{
doFailureBlock(error, failureBlock);
}];
}
};
/********************************
處理數據,緩存邏輯
*/
if (cachePolicy == TTVRequestCachePolicyNormal) {
id cacheData = [yyCache objectForKey:cacheKeyFinal];
if (cacheData) {
//返回緩存
doSuccessBlock(cacheData, YES);
} else {
//請求服務器
requestBlock(isGetOrPost);
}
} else if (cachePolicy == TTVRequestCachePolicyOnline) {
//請求服務器
requestBlock(isGetOrPost);
} else if (cachePolicy == TTVRequestCachePolicyHybrid) {
//返回緩存
id cacheData = [yyCache objectForKey:cacheKeyFinal];
if (cacheData) {
doSuccessBlock(cacheData, YES);
}
//請求服務器,再返回一次
requestBlock(isGetOrPost);
}
//返回空
return nil;
}
```