iOS: ¿Cómo puedo recibir HTTP 401 en lugar de -1012 NSURLErrorUserCancelledAuthentication

Tengo un problema similar al descrito en el siguiente enlace.

NSHTTPURLResponse statusCode regresa a cero cuando debería ser 401

Yo uso [NSURLConnection sendSynchronousRequest:returningResponse:error:] para obtener datos de un servidor.

Cuando NSURLConnection recibe el código HTTP 401, no devuelve nada más que un objeto de error con el código -1012 del NSURLErrorDomain. -1012 corresponde a NSURLErrorUserCancelledAuthentication . Como tengo que analizar el encabezado HTTP, necesito obtener el error original y no lo que NSURLConnection hizo de él.

¿Hay alguna manera de recibir el paquete original 401 http?

Sí. Deja de usar la API síncrona. Si usa la API asíncrona basada en delegates, tendrá mucho más control sobre la conexión. Con esta API, excepto en los casos en que se encuentra un error antes de que se reciba el encabezado HTTP, siempre recibirá -connection:didReceiveResponse: que le da acceso a los campos de encabezado HTTP (encapsulados en un objeto NSURLResponse ). También puede implementar autenticación utilizando los métodos delegates relevantes si así lo desea.

Solución:

 [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue new] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSHTTPURLResponse *aResponse = (NSHTTPURLResponse *)response; int statusCodeResponse = aResponse.statusCode; NSString *strError = [NSString stringWithFormat:@"%@", [connectionError description]]; if ([strError rangeOfString:@"Code=-1012"].location != NSNotFound) { statusCodeResponse = 401; } 

No es la mejor solución, pero funciona!

Me sorprende la respuesta “recomendada” a este hilo.

Bien, estoy seguro de que usar los métodos de versión asíncrona es mejor, pero aún no explica por qué la función sendSynchronousRequest te permite pasar una variable para devolver el código de respuesta, pero en algunas circunstancias, simplemente devuelve nada.

Son 4 años desde que se informó este problema, estoy usando XCode 6.2 con iOS 8.2, y este antiguo error todavía está presente.

Mis servicios web devuelven deliberadamente un error 401 cuando el nombre de usuario y la contraseña de un usuario no son correctos.

Cuando mi aplicación de iPhone llama a este servicio (con las credenciales incorrectas) …

 NSHTTPURLResponse *response = nil; NSData *data = [ NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error ]; 

.. la response se devuelve como nula (por lo que no puedo probar la Respuesta HTTP 401), el error recibe un mensaje -1012 envuelto así:

  Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn't be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x174869940 {NSErrorFailingURLStringKey=https://mywebservice.com/Service1.svc/getGroupInfo/6079, NSUnderlyingError=0x174e46030 "The operation couldn't be completed. (kCFErrorDomainCFNetwork error -1012.)", NSErrorFailingURLKey=https://mywebservice.com/Service1.svc/getGroupInfo/6079} 

y la función sendSynchronousRequest devuelve una larga cadena XML que contiene … bueno … esto

     Request Error    

Request Error

The server encountered an error processing the request. The exception message is 'Access is denied.'. See server logs for more details. The exception stack trace is:

at System.ServiceModel.Dispatcher.AuthorizationBehavior.Authorize(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc) at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

Vamos, Apple, arregla tus errores …

Es realmente simple. Ahora bien, es cierto que la solicitud asincrónica es realmente una buena ayuda. Pero debes tener en cuenta que es solo una ayuda. Http es solo un protocolo para la creación de redes y, por lo tanto, NO DEBE interactuar con el usuario. En otras palabras, ambos casos son uno y el mismo.

Si no desea utilizar el asistente de sincronización, le sugiero que muestre al usuario un cuadro de diálogo de inicio de sesión y repita su solicitud hasta que el usuario presione cancelar.

[editar]

solo por información, personalmente prefiero curl. Funciona como un encanto en casi todas partes;)

Estaba enfrentando el problema de no recibir el código de error 401 No autorizado (recibía una respuesta nula y el método didReceiveResponse no se llamaba) en vez de recibir el error cancelado -999. El error que estaba cometiendo en mi código es: En didReceiveChallenge: delegar método,

 if (challenge.previousFailureCount == 0) { //handle certificate trust completionHandler (NSURLSessionAuthChallengeUseCredential, newCredential); } else { completionHandler (NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); } 

El NSURLSessionAuthChallengeCancelAuthenticationChallenge daba como resultado el error -999 y no se obtuvo ninguna respuesta 401. En cambio, cuando utilicé completionHandler (NSURLSessionAuthChallengePerformDefaultHandling, nil); Recibí 401 respuestas en didReceiveResponse: delegar método como se esperaba.

Nota de la documentación de iOS https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/NSURLSessionConcepts/NSURLSessionConcepts.html , Si cancelamos la sesión de URL, se informará como cancelada, pero no como un error 401 en respuesta.

 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.