在处理 http 请求时,从基础开始是有帮助的,例如请求实际上是由操作系统发出的,是否收到了响应?这将在一定程度上帮助回答您关于检查请求是否遵守设置参数的问题。
我会挑战你在短语中使用“继承”这个词
有什么方法可以检查我设置的那些参数是否被请求遵守和继承?
面向对象编程中的继承具有非常特定的含义。您是否实际上创建了具有特定属性的 NSURLRequest 的自定义子类(我们称之为 SubClassA),然后是另一个子类(我们称之为 SubClassB),并期望第二个子类(SubClassB)从其父类(SubClassA)继承属性? 如果是这样,那么您提供的代码中肯定没有说明这一点。
有几个可用的 HTTP 代理程序可以帮助确认是否发送了 HTTP 请求,是否收到了响应,还允许您检查请求和响应的详细信息。Charles HTTP Proxy就是这样一个程序。使用 Charles,我能够确定您提供的代码没有发出任何 HTTP 请求。因此,如果未发出请求,您将无法确认或拒绝任何参数。
通过将包括 CustomProtocol 在内的行注释掉作为 NSURLSession 配置的一部分,并在有或没有这些行的情况下运行您的代码,我获得了一些潜在的有价值的信息:
- 通过注释掉包括 CustomProtocol 在内的行,实际上发出了请求(但失败了),正如 Charles HTTP 代理所告知的那样。我还在您的方法中添加了一个完成块
dataTaskWithRequest
。当 CustomProtocol 配置行被注释掉时,这个完成块被命中。CustomProtocol 的startLoading
方法没有命中。
- 当在原始行中使用 CustomProtocol 配置 NSURLSession 时,Charles HTTP Proxy 没有记录请求,并且完成处理程序未命中。但是,CustomProtocol 的
startLoading
方法被击中。
请参阅下面的代码(对原始问题中发布的代码进行的修改)。
NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSMutableArray *protocolsArray = [NSMutableArray arrayWithArray:config.protocolClasses];
//[protocolsArray insertObject:[CustomProtocol class] atIndex:0];
//config.protocolClasses = protocolsArray;
// ex. set some random parameters
[config setHTTPAdditionalHeaders:@{@"Authorization":@"1234"}];
[config setAllowsCellularAccess:NO];
[config setRequestCachePolicy:NSURLRequestReturnCacheDataElseLoad];
[config setHTTPShouldSetCookies:NO];
[config setNetworkServiceType:NSURLNetworkServiceTypeVoice];
[config setTimeoutIntervalForRequest:4321];
// Create a request with this configuration and start a task
NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://google.com"]];
NSURLSessionDataTask* task = [session dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSString * auth = [request valueForHTTPHeaderField:@"Authorization"];
NSLog(@"Authorization: %@", auth);
BOOL allowsCellular = [request allowsCellularAccess];
NSString * allowsCellularString = allowsCellular ? @"YES" : @"NO";
NSLog(@"Allows cellular: %@", allowsCellularString);
}];
[task resume];
这为您提供了 CustomProtocol 未正确处理请求的信息。是的,当 CustomProtocol 配置为 NSURLSession 的一部分时,方法内部的断点startLoading
会被命中,但这并不能明确证明 CustomProtocol 正在正确处理请求。正如 Apple(协议支持、NSURLProtocol 类参考)所述,使用 CustomProtocol 需要执行许多步骤,您应该确认您正在遵循这些步骤。
一些事情要确保工作:
- 如果您使用的是 CustomProtocol,这意味着您可能会尝试处理除 http、https、ftp、ftps 等之外的其他协议。
- 确保您的端点(正在侦听 http 请求和响应的服务器)实际上可以接受请求并回复。
- 如果您正在设置 HTTP 授权标头,请确保服务器可以正确响应,并且如果您期望得到肯定的响应,则凭据是有效的
- 记得注册你的 CustomProtocol
例如:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[NSURLProtocol registerClass:[CustomProtocol class]];
return YES;
}
下面是一个单元测试,以验证它NSURLSession
是否按预期运行(没有明确使用我们的自定义协议)。请注意,当将这个单元测试添加到 Apple 自己的CustomHTTPProtocol项目示例代码中时,该单元测试确实通过了,但使用我们非常简单的框架并没有通过CustomProtocol
- (void)testNSURLSession {
XCTestExpectation *expectation = [self expectationWithDescription:@"Testing standard NSURL Session"];
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com/"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
#pragma unused(data)
XCTAssertNil(error, @"NSURLSession test failed with error: %@", error);
if (error == nil) {
NSLog(@"success:%zd / %@", (ssize_t) [(NSHTTPURLResponse *) response statusCode], [response URL]);
[expectation fulfill];
}
}] resume];
[self waitForExpectationsWithTimeout:3.0 handler:^(NSError * _Nullable error) {
if(nil != error) {
XCTFail(@"NSURLSession test failed with error: %@", error);
}
}];
}
下面是一个单元测试,在使用我们自己的类进行配置时,可用于验证对 a 所做的配置NSURLSession
是否符合预期。CustomProtocol
再次请注意,使用空实现时此测试会失败,CustomProtocol
但如果使用测试驱动开发(首先创建测试,然后是允许测试通过的代码),这是预期的。
- (void)testCustomProtocol {
XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Custom Protocol"];
NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSMutableArray *protocolsArray = [NSMutableArray arrayWithArray:config.protocolClasses];
[protocolsArray insertObject:[CustomProtocol class] atIndex:0];
config.protocolClasses = protocolsArray;
// ex. set some random parameters
[config setHTTPAdditionalHeaders:@{@"Authorization":@"1234"}];
[config setAllowsCellularAccess:NO];
[config setRequestCachePolicy:NSURLRequestReturnCacheDataElseLoad];
[config setHTTPShouldSetCookies:NO];
[config setNetworkServiceType:NSURLNetworkServiceTypeVoice];
[config setTimeoutIntervalForRequest:4321];
// Create a request with this configuration and start a task
NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.apple.com"]];
NSURLSessionDataTask* task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
#pragma unused(data)
XCTAssertNil(error, @"test failed: %@", error.description);
if (error == nil) {
NSLog(@"success:%zd / %@", (ssize_t) [(NSHTTPURLResponse *) response statusCode], [response URL]);
NSString * auth = [request valueForHTTPHeaderField:@"Authorization"];
NSLog(@"Authorization: %@", auth);
XCTAssertNotNil(auth);
BOOL allowsCellular = [request allowsCellularAccess];
XCTAssertTrue(allowsCellular);
XCTAssertEqual([request cachePolicy], NSURLRequestReturnCacheDataElseLoad);
BOOL shouldSetCookies = [request HTTPShouldHandleCookies];
XCTAssertTrue(shouldSetCookies);
XCTAssertEqual([request networkServiceType], NSURLNetworkServiceTypeVoice);
NSTimeInterval timeOutInterval = [request timeoutInterval];
XCTAssertEqualWithAccuracy(timeOutInterval, 4321, 0.01);
[expectation fulfill];
}
}];
[task resume];
[self waitForExpectationsWithTimeout:3.0 handler:^(NSError * _Nullable error) {
if(nil != error) {
XCTFail(@"Custom Protocol test failed with error: %@", error);
}
}];
}