NSURLConnection возвращает ошибку вместо ответа для 401

У меня есть веб-API, который для конкретного запроса возвращает код состояния 200, если все прошло нормально, и 401, если пользователь не вошел в систему на основе токена авторизации. Все работает нормально, если статус ответа 200, но, кажется, не работает должным образом, если статус ответа 401, возвращая ошибку соединения с кодом -1012, в то время как ответ равен нулю.

Итак, следующий код:

[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    NSLog(@"%@", response);
    NSLog(@"%@", connectionError);

    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
    int statusCode = (int)[httpResponse statusCode];
    NSLog(@"response status code: %d", statusCode);

будет отображать

2015-04-01 15:58:18.511 MyProject[3618:694604] <NSHTTPURLResponse: 0x155facc0> { URL: *SOME_URL* } { status code: 200, headers {
    "Access-Control-Allow-Headers" = "Content-Type, Accept, X-Requested-With";
    "Access-Control-Allow-Methods" = "POST, GET, PUT, UPDATE, OPTIONS";
    "Access-Control-Allow-Origin" = "*";
    Connection = "keep-alive";
    "Content-Type" = "application/json";
    Date = "Wed, 01 Apr 2015 12:58:14 GMT";
    Server = "Wildfly 8";
    "Transfer-Encoding" = Identity;
    "X-Powered-By" = "Undertow 1";
} }
2015-04-01 15:58:18.513 MyProject[3618:694604] (null)
2015-04-01 15:58:18.513 MyProject[3618:694604] response status code: 200

если статус ответа 200, а если код состояния 401, я получу:

2015-04-01 16:05:55.988 MyProject[3633:695836] (null)
2015-04-01 16:05:55.992 MyProject[3633:695836] Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x146137c0 {NSErrorFailingURLKey=*SOME_URL*, NSErrorFailingURLStringKey=*SOME_URL*, NSUnderlyingError=0x1459e6d0 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1012.)"}
2015-04-01 16:05:55.992 MyProject[3633:695836] response status code: 0

Если я сделаю тот же запрос, используя Postman или устройство Android, я получу код состояния 401 со следующими заголовками (скопировано из Postman):

Connection → keep-alive
Content-Length → 30
Content-Type → application/json
Date → Wed, 01 Apr 2015 13:07:34 GMT
Server → Wildfly 8
X-Powered-By → Undertow 1

Есть ли какое-нибудь исправление или, возможно, библиотека, которая могла бы дать мне точный статус ответа? Я искал немного об ошибке -1012, но не смог найти много, и я не хочу основываться на этом.

Изменить: после небольшого исследования я нашел следующее утверждение в документации Appl: "Если для загрузки запроса требуется аутентификация, необходимо указать необходимые учетные данные как часть URL-адреса. Если аутентификация не удалась или учетные данные отсутствуют, Соединение будет пытаться продолжить без учетных данных."

Но тогда как я могу узнать, будет ли эта ошибка после статуса 401? Может ли он появиться после запроса другого типа?

4 ответа

Решение

Для того, чтобы получить код состояния 401, я думаю, вам нужно реализовать протокол NSURLConnectionDelegate а потом connection:didReceiveAuthenticationChallenge:,

Итак, вам также нужно будет передать делегат, возможно, используя [NSURLConnection connectionWithRequest:request delegate:self],

И, если вы не пытаетесь реализовать проверку подлинности, я бы всегда возвращал код состояния 200, но с другим содержимым json.

Надеюсь, это поможет.

Чтобы проверить ошибку 401, вы можете сделать это:

if (error != nil && error.code == NSURLErrorUserCancelledAuthentication) {
    // do something for 401 error
}

надеюсь, это поможет

На основе документации Apple https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/NSURLSessionConcepts/NSURLSessionConcepts.html,

Note: NSURLSession does not report server errors through the error parameter. The only errors your delegate receives through the error parameter are client-side errors, such as being unable to resolve the hostname or connect to the host. The error codes are described in URL Loading System Error Codes.
Server-side errors are reported through the HTTP status code in the NSHTTPURLResponse object. For more information, read the documentation for the NSHTTPURLResponse and NSURLResponse classes.

Нам нужно убедиться, что мы не отменяем сеанс в нашем коде. Например, я звонил NSURLSessionAuthChallengeCancelAuthenticationChallenge в делегатском методе didReceiveChallenge, когда previousFailureCount > 1. Это подавляло ответ 401 и также вызывало didReceiveResponse.

Когда я изменил выше значение на NSURLSessionAuthChallengePerformDefaultHandlingЯ получаю 401 несанкционированный ответ в методе делегата didReceiveResponse и работает как ожидалось.

У меня есть веб-API, который для конкретного запроса возвращает код состояния 200, если все прошло нормально, и 401, если пользователь не вошел в систему на основе токена авторизации. Все работает нормально, если статус ответа 200, но, кажется, не работает должным образом, если статус ответа 401, возвращая ошибку соединения с кодом -1012, в то время как ответ равен нулю.

Я столкнулся с той же самой проблемой, вызов REST API возвращает 401 в Android и Postman, но код состояния 0 в iOS с ошибкой подключения с кодом -1012.

Вы можете найти больше информации об этой проблеме в этом посте SO.

Кажется, это ошибка iOS (или, по крайней мере, очень странный подход), возникающая как с асинхронными, так и с синхронизирующими запросами.

Я публикую это на всякий случай, если это может быть полезно для других, сталкивающихся с той же проблемой. То, что я сделал в коде для управления состоянием 401 сразу после запроса, - это вызов метода, который проверяет каждый управляемый код состояния http.

Метод получает (NSError **) ошибку и [(NSHTTPURLResponse *) ответ statusCode].

Это часть 401 - используется информация об ошибках, хранящаяся в структуре userInfo.

// Auth failed or token problems
if (statusCode == 0 && [error userInfo] != nil) {

    // Get error detailed informations
    NSDictionary *userInfo = [error userInfo];
    NSString *errorString = [[userInfo objectForKey:NSUnderlyingErrorKey] localizedDescription];

    // If error message is of type authFailed or returns iOS code -1012 meaning auth failed
    if ([errorString containsString:@"kCFErrorDomainCFNetwork"] || [errorString containsString:@"-1012"]) {
        NSLog(@"%@ - ConnectionError - Code 401 - Cause: authentication failed, token is invalid or expired", type);
    } else {
        NSLog(@"%@ - ConnectionError - Cause: generic auth error", type);
    }

    // Alert user that auth failed
    ...
}

Другой способ - напрямую проверить этот код ошибки (-1012), как предложено @ThuanDINH выше. (отредактировал ответ для цели c).

Мой код можно изменить на:

// Auth failed - token problems
if (statusCode == 0 && [error code] == NSURLErrorUserCancelledAuthentication) {

    // If error code is UserCanceledAuthentication
    MPLog(MPLogLevelInfo, @"%@ - ConnectionError - Cause: authentication failed, token is invalid or expired", type);

    // Alert user that auth failed
    ...
}

Но таким образом вы не будете обрабатывать все другие ошибки (вам нужно включить каждый код NSURLError).

Здесь вы можете найти вопрос SO со списком всех кодов ошибок NSURLE.

Другие вопросы по тегам