魏名华

不要偷懒,做更好的自己

Nothing


No Welcome Message

Drdnetworking笔记 20170125

DRDNetworking笔记

饿了么开源的一个网络库DRDNetworking,基于AFNetworking3.0

  • 如果采取集中式的API设计,相对应JSON-RPC会产生大量的RPC协议封装代码。并且对于不同版本、类型的RPC协议,需要有不同的集中函数或者增加大量的参数来处理其中的差异性。
  • 如果采取分布式的API设计,则可以将这部分协议代码放进API自身类中来进行处理。在这里,我设计了一个RPCProtocol,由业务方自己来定义所需要遵循的业务RPC标准。而每个API都保存一个rpcDelegate字段来自定义自己的上层协议,而如果为空时,即代表着不进行RPC封装而是直接发送,从而达到JSON-RPCRESTFUL在一个APP共存的目的;并且由于每个API都可以指定不同的rpcDelegate,因此可以适用于服务器端不同的RPC版本兼容性。

总的来说,并不怎么样啊!– 20170125

JSON-RPC API

NS_ASSUME_NONNULL_BEGIN
@protocol DRDRPCProtocol <NSObject>
- (nullable NSString *)rpcRequestUrlWithAPI:(DRDBaseAPI *)api;
- (nullable id)rpcRequestParamsWithAPI:(DRDBaseAPI *)api;
- (nullable id)rpcResponseObjReformer:(id)responseObject withAPI:(DRDBaseAPI *)api;
- (nullable id)rpcResultWithFormattedResponse:(id)formattedResponseObj withAPI:(DRDBaseAPI *)api;
- (NSError *)rpcErrorWithFormattedResponse:(id)formattedResponseObj withAPI:(DRDBaseAPI *)api;
@end
NS_ASSUME_NONNULL_END

Protocol中会对RequestURLRequestParams进行RPC装箱设计,并且对于回包,也有rpcResponseObjReformer进行拆箱,将可用值和错误值交给rpcResultWithFormattedResponse以及rpcErrorWithFormattedResponse处理后,再返回给业务上层。 通过使用RPCProtocol,我们保持了整个网络层上层协议的一种可扩展性。

Example

下面的例子中,DRDJsonRpcVersionTwo将请求封装为:

Printing description of request:
<NSMutableURLRequest: 0x7b894d20> { URL: http://www.raboof.com/projects/jayrock/demo.ashx?test }

Printing description of parameters:
{
    id = "20dd6cc5-55bd-419f-95b3-77fc6459ccc8";
    jsonrpc = "2.0";
    method = add;
    params =     {
        a = 12;
        b = 1212;
    };
}

返回:

Printing description of responseObject:
{
    id = "a1bda1d7-22c7-4fbd-a4ba-d5fb1c44a13e";
    result = 1224;
}

Code:

DRDGeneralAPI *plusAPI = [[DRDGeneralAPI alloc]init];
    plusAPI.baseUrl        = @"http://www.raboof.com/projects/jayrock/demo.ashx?test";
    plusAPI.requestMethod  = @"add";
    plusAPI.requestParameters = @{
                                  @"a" : @(a),
                                  @"b" : @(b)
                                  };
    /// 使用JSON-RPC /// 
    plusAPI.rpcDelegate    = [DRDJsonRpcVersionTwo sharedJsonRpcVersionTwo]; 
    
    __weak typeof(self) weakSelf = self;
    [plusAPI setApiCompletionHandler:^(NSNumber * responseObject, NSError * error) {
        if (error) {
            [[[UIAlertView alloc] initWithTitle:@"Error"
                                       message:[NSString stringWithFormat:@"%@", error.localizedDescription]
                                      delegate:nil
                             cancelButtonTitle:@"OK"
                              otherButtonTitles:nil, nil]show];
        } else {
            weakSelf.labelResult.text = [NSString stringWithFormat:@"%@", responseObject];
        }
    }];
    [plusAPI start];
@interface DRDJsonRpcVersionTwo : NSObject<DRDRPCProtocol>

+ (nullable instancetype)sharedJsonRpcVersionTwo;

@end

static DRDJsonRpcVersionTwo *sharedInstance = nil;

@implementation DRDJsonRpcVersionTwo

+ (instancetype)sharedJsonRpcVersionTwo {
    if (!sharedInstance) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sharedInstance = [[DRDJsonRpcVersionTwo alloc] init];
        });
    }
    return sharedInstance;
}

#pragma mark - Protocol RPC
- (NSString *)rpcRequestUrlWithAPI:(DRDBaseAPI *)api {
    if (api.customRequestUrl) {
        return api.customRequestUrl;
    } else {
        return api.baseUrl;
    }
}

/// 封装JSON-RPC请求参数 /// 
- (id)rpcRequestParamsWithAPI:(DRDBaseAPI *)api {
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    dic[@"jsonrpc"]          = @"2.0";
    dic[@"id"]   = [[[NSUUID UUID] UUIDString] lowercaseString];
    if ([api requestMethod]) {
        dic[@"method"] = [api requestMethod];
    }
    if ([api requestParameters]) {
        dic[@"params"] = [api requestParameters];
    }
    
    NSDictionary *apiAdditionalParams = [api apiAddtionalRPCParams];
    [apiAdditionalParams enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL * stop) {
        [dic setObject:obj forKey:key];
    }];
    return [dic copy];
}

- (id)rpcResponseObjReformer:(id)responseObject withAPI:(nonnull DRDBaseAPI *)api {
    NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
    [((NSDictionary *)responseObject) enumerateKeysAndObjectsWithOptions:NSEnumerationReverse
                                                              usingBlock:^(id key, id obj, BOOL * stop) {
                                                                  if ([key isEqualToString:@"result"] ||
                                                                      [key isEqualToString:@"error"]) {
                                                                      if (obj && ![obj isEqual:[NSNull null]]) {
                                                                          dic[key] = obj;
                                                                      } else {
                                                                          dic[key] = [NSNull null];
                                                                      }
                                                                  }
                                                              }];
    return [dic copy];

}

- (id)rpcResultWithFormattedResponse:(id)formattedResponseObj withAPI:(nonnull DRDBaseAPI *)api {
    return [((NSDictionary *)formattedResponseObj) objectForKey:@"result"];
}

// 如果需要对某些错误进行统一处理,可以在本方法中进行处理
- (NSError *)rpcErrorWithFormattedResponse:(id)formattedResponseObj withAPI:(nonnull DRDBaseAPI *)api {
    id errorInfo = [((NSDictionary *)formattedResponseObj) objectForKey:@"error"];
    if (errorInfo && ![errorInfo isEqual:[NSNull null]]) {
        return errorInfo;
    } else {
        return nil;
    }
}

@end

SessionManager

由APIManager来负责提供SessionManager的策略(NSCache)。并且通过Global的配置,来决定每个Session的最大并发数。同时将AFNetworking封装进整个APIManager,保持对外透明。这样如果未来如果升级AFNetworking版本,或者打算切换到直接使用NSURLSession来处理网络连接,上层业务API也不需要有任何的改动,进一步增强了未来的可配置性。

/// SessionManager的缓存 ///
AFHTTPSessionManager *sessionManager;
    sessionManager = [self.sessionManagerCache objectForKey:baseUrlStr];
    if (!sessionManager) {
        sessionManager = [self newSessionManagerWithBaseUrlStr:baseUrlStr];
        [self.sessionManagerCache setObject:sessionManager forKey:baseUrlStr];
    }
    
/// Session的最大并发数控制 ///
- (AFHTTPSessionManager *)newSessionManagerWithBaseUrlStr:(NSString *)baseUrlStr {
    NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration];
    if (self.configuration) {
        sessionConfig.HTTPMaximumConnectionsPerHost = self.configuration.maxHttpConnectionPerHost;
    } else {
        sessionConfig.HTTPMaximumConnectionsPerHost = MAX_HTTP_CONNECTION_PER_HOST;
    }
    return [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:baseUrlStr]
                                    sessionConfiguration:sessionConfig];
}

检查用户频繁发送同一个请求,使用的错误提示文字

if ([self.sessionTasksCache objectForKey:hashKey]) {
        NSString *errorStr     = self.configuration.frequentRequestErrorStr; // 频繁发送同一个请求提示
        NSDictionary *userInfo = @{
                                   NSLocalizedDescriptionKey : errorStr
                                   };
        NSError *cancelError = [NSError errorWithDomain:NSURLErrorDomain
                                                   code:NSURLErrorCancelled
                                               userInfo:userInfo];
        [self callAPICompletion:api obj:nil error:cancelError];
        if (completionGroup) {
            dispatch_group_leave(completionGroup);
        }
        return;
    }