2019-07-01 NSURLProtocol 拦截 NSURLSession 请求时body丢失问题解决方案探讨

原文:https://blog.csdn.net/yunqiinsight/article/details/80134267

https://github.com/ChenYilong/CYLCURLNetworking?spm=a2c4e.11153959.blogcont543412.13.1bda7efeTdgxCQ

NSURLProtocol 拦截 NSURLSession 请求时body丢失问题解决方案探讨

在支持POST请求过程中会遇到丢失 body的 问题,有以下几种解决方法:

换用 NSURLConnection。NSURLConnection 与 NSURLSession

相比会遇到较多的性能问题,同时Apple的一些新特性也无法使用,终究会被淘汰,不作考虑。

body放header的方法,2M以下没问题,超过2M会导致请求延迟,超过 10M 就直接 Request timeout。而且无法解决

Body 为二进制数据的问题,因为Header里都是文本数据。

换用 Get 请求,不使用 Post 请求。这个也是可行的,但是毕竟对请求方式有限制,终究还是要解决 Post

请求所存在的问题。如果是基于旧项目做修改,则侵入性太大。这种方案适合新的项目。

另一种方法是我们下面主要要讲的,使用 HTTPBodyStream 获取 body,并赋值到 body

中,具体的代码如下,可以解决上面提到的问题:


//

//  NSURLRequest+CYLNSURLProtocolExtension.h

//

//

//  Created by ElonChan on 28/07/2017.

//  Copyright © 2017 ChenYilong. All rights reserved.

//

#import

@interface NSURLRequest (CYLNSURLProtocolExtension)

- (NSURLRequest *)cyl_getPostRequestIncludeBody;

@end

//

//  NSURLRequest+CYLNSURLProtocolExtension.h

//

//

//  Created by ElonChan on 28/07/2017.

//  Copyright © 2017 ChenYilong. All rights reserved.

//

#import "NSURLRequest+CYLNSURLProtocolExtension.h"

@implementation NSURLRequest (CYLNSURLProtocolExtension)

- (NSURLRequest *)cyl_getPostRequestIncludeBody {

    return [[self cyl_getMutablePostRequestIncludeBody] copy];

}

- (NSMutableURLRequest *)cyl_getMutablePostRequestIncludeBody {

    NSMutableURLRequest * req = [self mutableCopy];

    if ([self.HTTPMethod isEqualToString:@"POST"]) {

        if (!self.HTTPBody) {

            NSInteger maxLength = 1024;

            uint8_t d[maxLength];

            NSInputStream *stream = self.HTTPBodyStream;

            NSMutableData *data = [[NSMutableData alloc] init];

            [stream open];

            BOOL endOfStreamReached = NO;

            //不能用 [stream hasBytesAvailable]) 判断,处理图片文件的时候这里的[stream hasBytesAvailable]会始终返回YES,导致在while里面死循环。

            while (!endOfStreamReached) {

                NSInteger bytesRead = [stream read:d maxLength:maxLength];

                if (bytesRead == 0) { //文件读取到最后

                    endOfStreamReached = YES;

                } else if (bytesRead == -1) { //文件读取错误

                    endOfStreamReached = YES;

                } else if (stream.streamError == nil) {

                    [data appendBytes:(void *)d length:bytesRead];

                }

            }

            req.HTTPBody = [data copy];

            [stream close];

        }

    }

    return req;

}

@end

在用于拦截请求的 NSURLProtocol 的子类中实现方法 +canonicalRequestForRequest: 并处理 request 对象:

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {

  return [request cyl_getPostRequestIncludeBody];

}

+[NSURLProtocol canInitWithRequest:] 负责筛选哪些网络请求需要被拦截

+[NSURLProtocol canonicalRequestForRequest:] 负责对需要拦截的网络请求NSURLRequest 进行重新构造。

这里有一个注意点:+[NSURLProtocol canonicalRequestForRequest:] 的执行条件是 +[NSURLProtocol canInitWithRequest:] 返回值为 YES。

注意在拦截 NSURLSession 请求时,需要将用于拦截请求的 NSURLProtocol 的子类添加到 NSURLSessionConfiguration 中,用法如下:

  NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];

    NSArray *protocolArray = @[ [CYLURLProtocol class] ];

    configuration.protocolClasses = protocolArray;

    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];

更新:处理之后还是无效,把post换成get。。

你可能感兴趣的:(2019-07-01 NSURLProtocol 拦截 NSURLSession 请求时body丢失问题解决方案探讨)