Swift Alamofire: Cómo obtener el código de estado de respuesta HTTP

106

Me gustaría recuperar el código de estado de respuesta HTTP (por ejemplo, 400, 401, 403, 503, etc.) en caso de fallas en las solicitudes (e idealmente también para los éxitos). En este código, estoy realizando la autenticación de usuario con HTTP Basic y quiero poder enviar un mensaje al usuario de que la autenticación falló cuando el usuario escribe mal su contraseña.

Alamofire.request(.GET, "https://host.com/a/path").authenticate(user: "user", password: "typo")
    .responseString { (req, res, data, error) in
        if error != nil {
            println("STRING Error:: error:\(error)")
            println("  req:\(req)")
            println("  res:\(res)")
            println("  data:\(data)")
            return
        }
        println("SUCCESS for String")
}
    .responseJSON { (req, res, data, error) in
        if error != nil {
            println("JSON Error:: error:\(error)")
            println("  req:\(req)")
            println("  res:\(res)")
            println("  data:\(data)")
            return
        }
        println("SUCCESS for JSON")
}

Desafortunadamente, el error producido no parece indicar que se haya recibido realmente un código de estado HTTP 409:

STRING Error:: error:Optional(Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo=0x7f9beb8efce0 {NSErrorFailingURLKey=https://host.com/a/path, NSLocalizedDescription=cancelled, NSErrorFailingURLStringKey=https://host.com/a/path})
  req:<NSMutableURLRequest: 0x7f9beb89d5e0> { URL: https://host.com/a/path }
  res:nil
  data:Optional("")
JSON Error:: error:Optional(Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo=0x7f9beb8efce0 {NSErrorFailingURLKey=https://host.com/a/path, NSLocalizedDescription=cancelled, NSErrorFailingURLStringKey=https://host.com/a/path})
  req:<NSMutableURLRequest: 0x7f9beb89d5e0> { URL: https://host.com/a/path }
  res:nil
  data:nil

Además, sería bueno recuperar el cuerpo HTTP cuando ocurre un error porque mi servidor pondrá una descripción textual del error allí.

Preguntas
¿Es posible recuperar el código de estado con una respuesta que no sea 2xx?
¿Es posible recuperar el código de estado específico con una respuesta 2xx?
¿Es posible recuperar el cuerpo HTTP en una respuesta que no sea 2xx?

¡Gracias!

GregT
fuente
1
Si no está autenticado, recibirá un -999 por diseño. No estoy seguro de por qué es esto o cómo se puede resolver ... ¿Resolviste esto?
Guido Hendriks

Respuestas:

187

Para usuarios de Swift 3.x / Swift 4.0 / Swift 5.0 con Alamofire> = 4.0 / Alamofire> = 5.0


response.response?.statusCode

Ejemplo más detallado:

Alamofire.request(urlString)
        .responseString { response in
            print("Success: \(response.result.isSuccess)")
            print("Response String: \(response.result.value)")

            var statusCode = response.response?.statusCode
            if let error = response.result.error as? AFError {  
                statusCode = error._code // statusCode private                 
                switch error {
                case .invalidURL(let url):
                    print("Invalid URL: \(url) - \(error.localizedDescription)")
                case .parameterEncodingFailed(let reason):
                    print("Parameter encoding failed: \(error.localizedDescription)")
                    print("Failure Reason: \(reason)")
                case .multipartEncodingFailed(let reason):
                    print("Multipart encoding failed: \(error.localizedDescription)")
                    print("Failure Reason: \(reason)")
                case .responseValidationFailed(let reason):
                    print("Response validation failed: \(error.localizedDescription)")
                    print("Failure Reason: \(reason)")

                    switch reason {
                    case .dataFileNil, .dataFileReadFailed:
                        print("Downloaded file could not be read")
                    case .missingContentType(let acceptableContentTypes):
                        print("Content Type Missing: \(acceptableContentTypes)")
                    case .unacceptableContentType(let acceptableContentTypes, let responseContentType):
                        print("Response content type: \(responseContentType) was unacceptable: \(acceptableContentTypes)")
                    case .unacceptableStatusCode(let code):
                        print("Response status code was unacceptable: \(code)")
                        statusCode = code
                    }
                case .responseSerializationFailed(let reason):
                    print("Response serialization failed: \(error.localizedDescription)")
                    print("Failure Reason: \(reason)")
                    // statusCode = 3840 ???? maybe..
                default:break
                }

                print("Underlying error: \(error.underlyingError)")
            } else if let error = response.result.error as? URLError {
                print("URLError occurred: \(error)")
            } else {
                print("Unknown error: \(response.result.error)")
            }

            print(statusCode) // the status code
    } 

(Alamofire 4 contiene un sistema de error completamente nuevo, busque aquí para más detalles)

Para usuarios de Swift 2.x con Alamofire> = 3.0

Alamofire.request(.GET, urlString)
      .responseString { response in
             print("Success: \(response.result.isSuccess)")
             print("Response String: \(response.result.value)")
             if let alamoError = response.result.error {
               let alamoCode = alamoError.code
               let statusCode = (response.response?.statusCode)!
             } else { //no errors
               let statusCode = (response.response?.statusCode)! //example : 200
             }
}
Alessandro Ornano
fuente
response.result.error puede darle un error de Alamofire, por ejemplo. StatusCodeValidationFailed, FAILURE: Error Domain=com.alamofire.error Code=-6003. Si realmente desea obtener un error de respuesta HTTP, es mejor para el usuarioresponse.response?.statusCode
Kostiantyn Koval
@KostiantynKoval Estoy de acuerdo contigo, has hecho una aclaración adecuada. Actualicé mi respuesta para mayor claridad
Alessandro Ornano
50

En el controlador de finalización con el argumento a responsecontinuación, encuentro que el código de estado http está en response.response.statusCode:

Alamofire.request(.POST, urlString, parameters: parameters)
            .responseJSON(completionHandler: {response in
                switch(response.result) {
                case .Success(let JSON):
                    // Yeah! Hand response
                case .Failure(let error):
                   let message : String
                   if let httpStatusCode = response.response?.statusCode {
                      switch(httpStatusCode) {
                      case 400:
                          message = "Username or password not provided."
                      case 401:
                          message = "Incorrect password for user '\(name)'."
                       ...
                      }
                   } else {
                      message = error.localizedDescription
                   }
                   // display alert with error message
                 }
wcochran
fuente
Hola, ¿statusCode 200 caerá en falla? mi solicitud se manejó correctamente en el lado del servidor, pero la respuesta cae bajo Fallo
perwyl
1
@perwyl No creo que 200 sea un error http: ver stackoverflow.com/questions/27921537/…
wcochran
2
El código de estado @perwyl 200 indica éxito, si su servidor devolvió 200 y la respuesta indica un error (es decir, alguna propiedad llamada issuccess = false), entonces debe manejar eso en su código de interfaz
Parama Dharmika
16
    Alamofire
        .request(.GET, "REQUEST_URL", parameters: parms, headers: headers)
        .validate(statusCode: 200..<300)
        .responseJSON{ response in

            switch response.result{
            case .Success:
                if let JSON = response.result.value
                {
                }
            case .Failure(let error):
    }
Abo3atef
fuente
Esto estimula las mejores prácticas de api
CESCO
URW :) Intente implementar enrutador para sus solicitudes. ;)
Abo3atef
11

La mejor forma de obtener el código de estado usando alamofire.

 Alamofire.request (URL) .responseJSON {
  respuesta en

  let status = response.response? .statusCode
  print ("ESTADO \ (estado)")

}
jamesdelacruz
fuente
5

Al responseJSONfinalizar, puede obtener el código de estado del objeto de respuesta, que tiene un tipo de NSHTTPURLResponse?:

if let response = res {
    var statusCode = response.statusCode
}

Esto funcionará independientemente de si el código de estado se encuentra en el rango de error. Para obtener más información, consulte la documentación de NSHTTPURLResponse .

Para su otra pregunta, puede usar la responseStringfunción para obtener el cuerpo de respuesta sin procesar. Puede agregar esto además de responseJSONy ambos serán llamados.

.responseJson { (req, res, json, error) in
   // existing code
}
.responseString { (_, _, body, _) in
   // body is a String? containing the response body
}
Sam
fuente
3

Su error indica que la operación se está cancelando por algún motivo. Necesitaría más detalles para entender por qué. Pero creo que el problema más grande puede ser que, dado que su punto final https://host.com/a/pathes falso, no hay una respuesta real del servidor para informar y, por lo tanto, está viendo nil.

Si llega a un punto final válido que ofrece una respuesta adecuada, debería ver un valor no nulo para res(usando las técnicas que Sam menciona) en forma de un NSURLHTTPResponseobjeto con propiedades como statusCode, etc.

Además, para que quede claro, errores de tipo NSError. Le dice por qué falló la solicitud de red. El código de estado del error en el lado del servidor es en realidad parte de la respuesta.

Espero que ayude a responder su pregunta principal.

pixeles de lluvia
fuente
Buena captura, estaba demasiado concentrado en la lista de preguntas al final.
Sam
1
En realidad, está recibiendo un código 401 No autorizado porque estoy enviando intencionalmente la contraseña incorrecta para simular que un usuario escribe mal su contraseña para poder detectar ese caso y dar comentarios al usuario. Esa no es la URL real que estoy usando, pero estoy usando una URL legítima que resulta exitosa cuando envío la contraseña correcta. La salida de la consola en mi publicación original es la salida real de ingresar a una URL real (excepto que la URL es falsa) y puede ver que el resobjeto lo es nil, por lo que esta respuesta no ayuda, lo siento.
GregT
Gracias por aclararlo. Bueno, entonces lo único que es sospechoso aquí es el error -999. No he encontrado eso, pero la documentación sugiere que la solicitud se está cancelando (por lo que la cuestión de incluso recibir una respuesta debería ser discutible). ¿Ha intentado imprimir el objeto de respuesta para un tipo diferente de error que no está relacionado con la autenticación? Sólo curioso.
rainypixels
ahhhh pensé que errorse refería a respuestas con códigos de estado que están fuera del rango que proporcionamos validate(). ¡¡¡Gracias!!!
Gerald
3

O usa la coincidencia de patrones

if let error = response.result.error as? AFError {
   if case .responseValidationFailed(.unacceptableStatusCode(let code)) = error {
       print(code)
   }
}
mbryzinski
fuente
Trabajado como un encanto.
alasker
3

puede consultar el siguiente código para el controlador de código de estado de alamofire

    let request = URLRequest(url: URL(string:"url string")!)    
    Alamofire.request(request).validate(statusCode: 200..<300).responseJSON { (response) in
        switch response.result {
        case .success(let data as [String:Any]):
            completion(true,data)
        case .failure(let err):
            print(err.localizedDescription)
            completion(false,err)
        default:
            completion(false,nil)
        }
    }

si el código de estado no es validado, se ingresará la falla en el caso del interruptor

Amr enojado
fuente
1

Para usuarios de Swift 2.0 con Alamofire> 2.0

Alamofire.request(.GET, url)
  .responseString { _, response, result in
    if response?.statusCode == 200{
      //Do something with result
    }
}
Publicación de Gerrit
fuente
1

Necesitaba saber cómo obtener el número de código de error real.

Heredé un proyecto de otra persona y tuve que obtener los códigos de error de una .catchcláusula que habían configurado previamente para Alamofire:

} .catch { (error) in

    guard let error = error as? AFError else { return }
    guard let statusCode = error.responseCode else { return }

    print("Alamofire statusCode num is: ", statusCode)
}

O si necesita obtenerlo del responsevalor, siga la respuesta de @ mbryzinski

Alamofire ... { (response) in

    guard let error = response.result.error as? AFError else { return }
    guard let statusCode = error.responseCode else { return }

    print("Alamofire statusCode num is: ", statusCode)
})
Lance Samaria
fuente