Жизненный цикл NSURLSession и базовое разрешение

Я не могу последовательно читать ответ с сервера, если я использую код ниже.

Заголовок:

#import <Foundation/Foundation.h> @interface TestHttpClient : NSObject<NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDownloadDelegate> -(void)POST:(NSString*) relativePath payLoad:(NSData*)payLoad; @end 

Реализация:

 #import "TestHttpClient.h" @implementation TestHttpClient -(void)POST:(NSString*)relativePath payLoad:(NSData*)payLoad { NSURL* url = [NSURL URLWithString:@"http://apps01.ditat.net/mobile/batch"]; // Set URL credentials and save to storage NSURLCredential *credential = [NSURLCredential credentialWithUser:@"BadUser" password:@"BadPassword" persistence: NSURLCredentialPersistencePermanent]; NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:[url host] port:443 protocol:[url scheme] realm:@"Ditat mobile services endpoint" authenticationMethod:NSURLAuthenticationMethodHTTPBasic]; [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace]; // Configure session NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration]; sessionConfig.timeoutIntervalForRequest = 30.0; sessionConfig.timeoutIntervalForResource = 60.0; sessionConfig.HTTPMaximumConnectionsPerHost = 1; sessionConfig.URLCredentialStorage = [NSURLCredentialStorage sharedCredentialStorage]; // Should this line be here?? NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]]; // Create request object with parameters NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0]; // Set header data [request setHTTPMethod:@"POST"]; [request setValue:@"application/x-protobuf" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"Version 1.0" forHTTPHeaderField:@"User-Agent"]; [request setValue:@"Demo" forHTTPHeaderField:@"AccountId"]; [request setValue:@"1234-5678" forHTTPHeaderField:@"DeviceSerialNumber"]; [request setValue:@"iOS 7.1" forHTTPHeaderField:@"OSVersion"]; [request setHTTPBody:payLoad]; // Call session to post data to server?? NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request]; [downloadTask resume]; } -(void)invokeDelegateWithResponse:(NSHTTPURLResponse *)response fileLocation:(NSURL*)location { NSLog(@"HttpClient.invokeDelegateWithResponse - code %ld", (long)[response statusCode]); } #pragma mark - NSURLSessionDownloadDelegate - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSLog(@"NSURLSessionDownloadDelegate.didFinishDownloadingToURL"); [self invokeDelegateWithResponse:(NSHTTPURLResponse*)[downloadTask response] fileLocation:location]; [session invalidateAndCancel]; } // Implemented as blank to avoid compiler warning -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { } // Implemented as blank to avoid compiler warning -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { } 

Может быть вызван из любого VC (например, код места под действием кнопки)

 -(IBAction)buttonTouchUp:(UIButton *)sender { TestHttpClient *client = [[TestHttpClient alloc] init]; [client POST:@"" payLoad:nil]; return; } 

Если вы запустите программу и назовете этот код – она ​​будет показана в завершении NSLog с 401. Вторая попытка – не будет работать. Или может работать, если подождать немного. Но он не будет отправлять запросы сервера при нажатии кнопки.

NSURLSession каким-то образом «запоминает» неудачные попытки и ничего не вернет? Почему это поведение? Я хочу видеть 2 сообщения NSLog каждый раз, когда я нажимаю кнопку.

One Solution collect form web for “Жизненный цикл NSURLSession и базовое разрешение”

TL; DR; В вашем примере вы неправильно обрабатываете аутентификацию.

Это происходит, когда клиент iOS или MacOS встречает URL-адрес, требующий аутентификации:

  1. Клиент запрашивает ресурс с сервера GET www.example.com/protected

  2. Ответ сервера для этого запроса имеет код состояния 401 и включает заголовок WWW-Authenticate. Это говорит клиенту, что это защищенный ресурс, и указывает, какой метод аутентификации использовать для доступа к ресурсу. В iOS и MacOS это «проверка подлинности», на которую отвечает делегат. Заголовок WWW-Authenticate специально упоминается в документации, чтобы выделить это .

    • Обычно на iOS и MacOS, если делегат не предоставлен или не обрабатывает проблемы с проверкой подлинности, система загрузки URL будет пытаться найти соответствующие учетные данные для этого ресурса и типа проверки подлинности, посмотрев в NSURLCredentialStorage. Он ищет соответствующие учетные данные, которые были сохранены как значение по умолчанию.

    • Если предоставлен делегат, реализующий аутентификацию, этот делегат должен предоставить учетные данные для этого ресурса.

  3. Когда система имеет учетные данные для проверки подлинности, запрос снова выполняется с учетными данными.

    GET http://www.example.com/protected Авторизация: Basic blablahaala

Это объясняет поведение, которое вы видите в Чарльзе, и является правильным поведением в соответствии с различными спецификациями HTTP.

Очевидно, что если вы не хотите внедрять делегат для своего подключения, у вас есть возможность поместить учетные данные для ресурса, к которому вы обращаетесь в NSURLCredentialStorage. Система будет использовать это и не потребует от вас выполнения делегата для учетных данных.

Создайте NSURLCredential:

 credential = [NSURLCredential credentialWithUser:@"some user" password:@"clever password" persistence: NSURLCredentialPersistencePermanent]; 

NSURLCredentialPersistencePermanent сообщит NSURLCredentialStorage чтобы он постоянно NSURLCredentialStorage в NSURLCredentialStorage ключей. Существуют и другие возможные значения, которые вы можете использовать, например NSURLCredentialPersistenceForSession . Они описаны в документации. , Вам следует избегать использования NSURLCredentialPersistencePermanent с учетными данными, которые не были проверены, использовать сеанс или нет до тех пор, пока учетные данные не будут проверены. Вероятно, вы видели проекты с использованием «KeychainWrapper» или прямой доступ к API Keychain для сохранения имен и паролей в Интернете – это не самый предпочтительный способ сделать это, NSURLCredentialStorage .

Создайте NSURLProtectionSpace с правильным хостом, портом, протоколом, областью и методом проверки подлинности:

 protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:[url host] port:443 protocol:[url scheme] realm:@"Protected Area" authenticationMethod:NSURLAuthenticationMethodHTTPBasic]; 

Обратите внимание, что [[url port] integerValue] не будет предоставлять значения по умолчанию для HTTP (80) или HTTPS (443). Вы должны предоставить их. Область ДОЛЖНА соответствовать тому, что предоставляет сервер.

Наконец, поместите его в NSURLCredentialStorage :

 [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace]; 

Это позволит системе загрузки URL-адресов использовать эти учетные данные с этого момента. По сути, этот же процесс также можно использовать для справки доверия сервера SSL / TLS.

В вашем вопросе вы обрабатываете доверие сервера, но не NSURLAuthenticationMethodHTTPBasic . Когда ваше приложение получает вызов аутентификации для HTTP Basic Auth, вы не отвечаете на него, и оттуда все идет вниз. В вашем случае вам, вероятно, не нужно внедрять URLSession:didReceiveChallenge:completionHandler: вообще, если вы выполните описанные выше шаги, чтобы установить базовые учетные данные для проверки подлинности по умолчанию для этого защитного места. Система будет обрабатывать NSURLAuthenticationMethodServerTrust , выполнив NSURLAuthenticationMethodServerTrust по умолчанию. Затем система найдет учетные данные по умолчанию, которые вы установили для этого защитного места для базовой проверки подлинности, и используйте это.

ОБНОВИТЬ

Основываясь на новой информации в комментариях и запущенной модифицированной версией кода, OP фактически получает эту ошибку в ответ на свой запрос: NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813) Эта ошибка может быть найдена в SecureTransport.h , Корневой сертификат на учетных данных сервера не существует или не поддерживается системой. Это очень редко, но может случиться. Technote 2232 объясняет, как настроить проверку доверия сервера на клиенте, чтобы разрешить этот сертификат .

  • Метод завершения загрузки второй страницы NSURLSession, вызванный дважды после перезапуска
  • Перенаправление перехвата NSURLSession не работает
  • Как возобновить загрузку задач (NSURLSessionDownloadTask) после запуска приложения
  • NSURLSessionUploadTask, как читать ответ сервера
  • Приостановить, возобновить, Отменить загрузку с помощью NSURLSession UploadTask
  • NSURLSession - iOS убивает приложение в фоновом режиме при общении с сервером
  • AFNetworking 3.0 AFHTTPSessionManager с использованием NSOperation
  • Пакетная загрузка файлов с использованием NSURLSession с целостностью файла в iOS
  • NSURLSession didReceiveChallenge только один раз
  • iOS NSURLSessionConfiguration в версии Amazon S3 Upload-SSL
  • Получение данных JSON с использованием NSURLSession
  • Interesting Posts

    как создавать локальные уведомления в приложении iphone

    Как приложение Whatsapp удалило совместимость приложения для iPad?

    iOS – изменение ограничений привязки программно

    Swift – Получить местное время

    Как получить обновления местоположения для iOS 7 и 8 Даже когда приложение приостановлено

    /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/llvm-gcc-4.2 не удалось с кодом выхода 1

    Как реализовать SegmentedControlValueChange для управления представлением контейнера

    Как программно изменить размер UINavigationBar?

    Текстовые прокрутки вне границы окна UITextView

    Как установить CATextLayer в видео в соответствии с фреймом?

    Использование Firebase Analytics и Google Analytics вместе – iOS

    Составление видео и аудио с использованием AVMutableComposition

    Установка ключаWindow rootViewController, не работающего в iOS8

    Инструменты, Target Не удалось выполнить: Удаленное исключение: «Не удалось получить задачу для pid»

    Как заставить объект (узел SKSprite) исчезать при щелчке

    PhoneC: Разработка iOS проста с помощью XCode, Swift3, UITableView, cocatouch, давайте создадим приложения для iPhone, iPad и Macbook.