casatwy / CTNetworking

iOS networking API layer
Other
488 stars 104 forks source link

demo响应报文处理代码崩溃 #18

Closed Vincentzzg closed 5 years ago

Vincentzzg commented 5 years ago

直接下载仓库代码,运行点击send request,崩溃。 方法

2018-09-18 2 52 47 2018-09-18 2 56 07

我加了一些类型判断和容错的代码

Vincentzzg commented 5 years ago

这么快merge了😄

我又仔细看了代码,最新版本仓库代码由于默认的sessionManager的创建,没有指定responseSerializer

- (AFHTTPSessionManager *)sessionManagerWithService:(id<CTServiceProtocol>)service
{
    AFHTTPSessionManager *sessionManager = nil;
    if ([service respondsToSelector:@selector(sessionManager)]) {
        sessionManager = service.sessionManager;
    }
    if (sessionManager == nil) {
        sessionManager = [AFHTTPSessionManager manager];
    }
    return sessionManager;
}

而AFHTTPSessionManager实现中 self.responseSerializer = [AFJSONResponseSerializer serializer];默认是AFJSONResponseSerializer

- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }

    // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
        url = [url URLByAppendingPathComponent:@""];
    }

    self.baseURL = url;

    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];//默认AFJSONResponseSerializer

    return self;
}

而AFJSONResponseSerializer中对responseObjectForResponse方法的实现中已经对数据进行了转换,

#pragma mark - AFURLResponseSerialization

- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
        if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
            return nil;
        }
    }

    // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
    // See https://github.com/rails/rails/issues/1742
    BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];

    if (data.length == 0 || isSpace) {
        return nil;
    }

    NSError *serializationError = nil;

    id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];//这里已经对json数据进行了序列化,转成了对象
    ...
}

所以理论上DemoService里面的resultWithResponseData方法里面responseData会一直是NSDdictionary

YearRen commented 5 years ago

我觉得这里应该是两种都会有的,因为有时候后台返回的是json的有时候后台返回的是data类型 在CTApiProxy.m中,调AFNetworking请求的回调中responseData直接是用nsdata接收的,我觉得这里需要修改不知道我理解的对不对

Vincentzzg commented 5 years ago

我说下我的理解,其实后台返回的数据,客户端的网络框架拿到的报文都是NSData类型的数据,你说的“有时候后台返回的是json的有时候后台返回的是data类型”,是AFNetwork根据设置的responseSerializer做了数据转换,不同的响应序列化器会返回不同类型的数据,你可以自己测试下,往底层去跟。

可以看下AFURLSessionManager里面的代码中的实现

#pragma mark - NSURLSessionTaskDelegate

- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
...
casatwy commented 5 years ago

其实这边我的设计思路是让service来处理返回数据的。

service对象不光负责提供request,也负责解析该service的API的response。

https://github.com/casatwy/CTNetworking/blob/master/CTNetworking/CTNetworking/Services/CTServiceProtocol.h

在上面的service protocol中大家就能看到我的设计了。虽然demo这边怎么改都无所谓,但实际上还是要看service的设计初衷的。

如果按照这个设计初衷来,proxy就无所谓response是什么类型的数据了,因为最终都是交给service去处理的。

Vincentzzg commented 5 years ago

嗯嗯,能体会到田大哥的设计初衷,我是从最开始的RTNetworking框架开始用的,一直跟到现在的

昨天我确实没有考虑到这一层,考虑到这一层的话,修改的方向就不一样了,协议中的返回类型也要配合改掉,因为不确定返回值的类型,所以应该保持AFHTTPSessionManager的返回类型id _Nullable responseObject。

现在的返回类型NSData、还有下面几个key值的定义,都是默认proxy返回数据是JSON数据、NSData类型,对于使用者来说有很大的迷惑性

NSString * const kCTApiProxyValidateResultKeyResponseJSONObject = @"kCTApiProxyValidateResultKeyResponseJSONObject";
NSString * const kCTApiProxyValidateResultKeyResponseJSONString = @"kCTApiProxyValidateResultKeyResponseJSONString";
NSString * const kCTApiProxyValidateResultKeyResponseData = @"kCTApiProxyValidateResultKeyResponseData";

CTLogger类的方法名和参数也要微调下 + (NSString *)logDebugInfoWithResponse:(NSHTTPURLResponse *)response rawResponseData:(NSData *)rawResponseData responseString:(NSString *)responseString request:(NSURLRequest *)request error:(NSError *)error

等下我提交个新的PR给你看下

casatwy commented 5 years ago

嗯,之前的写法确实对使用者产生了迷惑~CTNetworking改造的时候我这一点也忽略了😂