原文: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。。