如何让 IOS 应用信任本地主机进行开发 API
How to get IOS app to trust localhost for development API
将 Visual Studio 用于 Mac,我创建了一个 RESFUL API(从 API 项目模板开发),开箱即用的输出非常简单JSON 文件(即 ["value1","value2"])位于以下 url:https://localhost:5001/api/values。
因此,虽然 运行 Visual Studio 上的 API 在后台运行,但我也在同一台计算机上 运行 XCode;因为我正在开发一个需要连接到 API url 并输出预期的 JSON 响应的应用程序。
问题是它由于信任错误而不断失败:"TIC SSL Trust Error...NSURLSession/NSURLConnection HTTP Load failed"。
根据我的研究,我认为我需要将本地主机的自签名证书安装到我的 XCode 模拟器上,以便它允许应用程序信任 url。如何访问此本地主机证书?
或者这是正确的方法吗?
产生信任错误的代码:
// Specify the base url...
static NSString *const API_URL = @"https://localhost:5001";
// Specify completed url ...
NSString *urlAppend = [NSString stringWithFormat:@"/api/values"];
NSString *urlStr = [[[NSString alloc] initWithString:API_URL]
stringByAppendingString:urlAppend];
// Specify the URL object that can return JSON, XML, etc....
NSURL *url = [[NSURL alloc] initWithString:urlStr];
// Create an NSURLSessionDataTask that does the GET request at the specified URL (using NSURLSession shared session)...
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Handle response (output respnose to log window))...
NSLog(@"%@", [output initWithData:data encoding:(NSASCIIStringEncoding)] ? : @"no data");
}];
// Start task ....
[task resume];
您可以尝试这样做:在发出任何 HTTP 请求的每个 ViewController 的顶部,就在导入部分下方,添加以下代码:
@interface NSURLRequest (DummyInterface)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host;
+ (void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString*)host;
@end
然后,在你发出请求的地方,调用这个:
[request setHTTPBody:data];
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSError *error;
NSHTTPURLResponse *response = nil;
NSData *data2 = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
解决此问题的最佳方法是 Perform Manual Server Trust Authentication for the url of the localhost development server (running in the background). This is done by utilizing the URLSession:didReceiveChallenge:completionHandler: delegate method of your NSURLSessionDelegate;这将使您有机会匹配本地主机 url 并在会话数据任务开始时手动信任它(在它因不可信 self-signed-certificate 而拒绝之前)。
方法如下:
第 1 步:将 NSURLSession 操作包装在 class 中,比如 "Networking",它实现了 NSURLSessionDelegate 协议;并添加手动信任本地主机 url(指定为 'DEV_URL')的委托方法。
// Networking.h
#import <Foundation/Foundation.h>
@interface Networking : NSObject <NSURLSessionDelegate>
- (void) fetchContentsOfUrl:(NSURL *)url
completion:(void (^)(NSData *data, NSError *error)) completionHandler;
@end
// Networking.m
#import "Networking.h"
static NSString *const DEV_URL = @"https://localhost:5001";
@implementation Networking
- (void) fetchContentsOfUrl:(NSURL *)url
completion:(void (^)(NSData * _Nullable, NSError * _Nullable))completionHandler {
NSURLSession *dataSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
NSURLSessionDataTask *dataTask = [dataSession
dataTaskWithURL:url
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (completionHandler == nil) {
return;
}
if (error){
completionHandler(nil, error);
return;
}
completionHandler(data, nil);
}];
[dataTask resume];
}
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSURL* baseURL = [NSURL URLWithString:DEV_URL];
if ([challenge.protectionSpace.host isEqualToString:baseURL.host]) {
NSLog(@"trusting connection to host %@", challenge.protectionSpace.host);
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
} else {
NSLog(@"Not trusting connection to host %@", challenge.protectionSpace.host);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
@end
第 2 步:用 Networking class 的实例化和调用 fetchContentsOfUrl:completion: 调用 NSURLSessionDataTask 的方法替换您的原始代码,如下所示:
Networking *networker = [[Networking alloc] init];
[networker fetchContentsOfUrl:url completion:^(NSData *data, NSError *error) {
if (error == nil) {
NSString *output = [NSString alloc];
NSLog(@"%@", [output initWithData:data encoding:(NSASCIIStringEncoding)] ? : @"no data");
}
}];
大功告成!!
将 Visual Studio 用于 Mac,我创建了一个 RESFUL API(从 API 项目模板开发),开箱即用的输出非常简单JSON 文件(即 ["value1","value2"])位于以下 url:https://localhost:5001/api/values。
因此,虽然 运行 Visual Studio 上的 API 在后台运行,但我也在同一台计算机上 运行 XCode;因为我正在开发一个需要连接到 API url 并输出预期的 JSON 响应的应用程序。
问题是它由于信任错误而不断失败:"TIC SSL Trust Error...NSURLSession/NSURLConnection HTTP Load failed"。
根据我的研究,我认为我需要将本地主机的自签名证书安装到我的 XCode 模拟器上,以便它允许应用程序信任 url。如何访问此本地主机证书?
或者这是正确的方法吗?
产生信任错误的代码:
// Specify the base url...
static NSString *const API_URL = @"https://localhost:5001";
// Specify completed url ...
NSString *urlAppend = [NSString stringWithFormat:@"/api/values"];
NSString *urlStr = [[[NSString alloc] initWithString:API_URL]
stringByAppendingString:urlAppend];
// Specify the URL object that can return JSON, XML, etc....
NSURL *url = [[NSURL alloc] initWithString:urlStr];
// Create an NSURLSessionDataTask that does the GET request at the specified URL (using NSURLSession shared session)...
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Handle response (output respnose to log window))...
NSLog(@"%@", [output initWithData:data encoding:(NSASCIIStringEncoding)] ? : @"no data");
}];
// Start task ....
[task resume];
您可以尝试这样做:在发出任何 HTTP 请求的每个 ViewController 的顶部,就在导入部分下方,添加以下代码:
@interface NSURLRequest (DummyInterface)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host;
+ (void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString*)host;
@end
然后,在你发出请求的地方,调用这个:
[request setHTTPBody:data];
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSError *error;
NSHTTPURLResponse *response = nil;
NSData *data2 = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
解决此问题的最佳方法是 Perform Manual Server Trust Authentication for the url of the localhost development server (running in the background). This is done by utilizing the URLSession:didReceiveChallenge:completionHandler: delegate method of your NSURLSessionDelegate;这将使您有机会匹配本地主机 url 并在会话数据任务开始时手动信任它(在它因不可信 self-signed-certificate 而拒绝之前)。
方法如下:
第 1 步:将 NSURLSession 操作包装在 class 中,比如 "Networking",它实现了 NSURLSessionDelegate 协议;并添加手动信任本地主机 url(指定为 'DEV_URL')的委托方法。
// Networking.h
#import <Foundation/Foundation.h>
@interface Networking : NSObject <NSURLSessionDelegate>
- (void) fetchContentsOfUrl:(NSURL *)url
completion:(void (^)(NSData *data, NSError *error)) completionHandler;
@end
// Networking.m
#import "Networking.h"
static NSString *const DEV_URL = @"https://localhost:5001";
@implementation Networking
- (void) fetchContentsOfUrl:(NSURL *)url
completion:(void (^)(NSData * _Nullable, NSError * _Nullable))completionHandler {
NSURLSession *dataSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
NSURLSessionDataTask *dataTask = [dataSession
dataTaskWithURL:url
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (completionHandler == nil) {
return;
}
if (error){
completionHandler(nil, error);
return;
}
completionHandler(data, nil);
}];
[dataTask resume];
}
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSURL* baseURL = [NSURL URLWithString:DEV_URL];
if ([challenge.protectionSpace.host isEqualToString:baseURL.host]) {
NSLog(@"trusting connection to host %@", challenge.protectionSpace.host);
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
} else {
NSLog(@"Not trusting connection to host %@", challenge.protectionSpace.host);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
@end
第 2 步:用 Networking class 的实例化和调用 fetchContentsOfUrl:completion: 调用 NSURLSessionDataTask 的方法替换您的原始代码,如下所示:
Networking *networker = [[Networking alloc] init];
[networker fetchContentsOfUrl:url completion:^(NSData *data, NSError *error) {
if (error == nil) {
NSString *output = [NSString alloc];
NSLog(@"%@", [output initWithData:data encoding:(NSASCIIStringEncoding)] ? : @"no data");
}
}];
大功告成!!