Swift Alamofire: Comment obtenir le code d'état de la réponse HTTP

106

Je voudrais récupérer le code d'état de la réponse HTTP (par exemple 400, 401, 403, 503, etc.) pour les échecs de demande (et idéalement pour les réussites aussi). Dans ce code, j'effectue une authentification utilisateur avec HTTP Basic et je souhaite pouvoir envoyer un message à l'utilisateur indiquant que l'authentification a échoué lorsque l'utilisateur a mal saisi son mot de passe.

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")
}

Malheureusement, l'erreur produite ne semble pas indiquer qu'un code d'état HTTP 409 a effectivement été reçu:

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

De plus, il serait bien de récupérer le corps HTTP lorsqu'une erreur se produit car mon serveur y mettra une description textuelle de l'erreur.

Questions
Est-il possible de récupérer le code d'état sur une réponse non-2xx?
Est-il possible de récupérer le code d'état spécifique sur une réponse 2xx?
Est-il possible de récupérer le corps HTTP sur une réponse non-2xx?

Merci!

GregT
la source
1
Si vous n'êtes pas authentifié, vous recevez un -999 par conception. Je ne sais pas pourquoi ou comment cela peut être résolu ... Avez-vous résolu ce problème?
Guido Hendriks

Réponses:

187

Pour les utilisateurs de Swift 3.x / Swift 4.0 / Swift 5.0 avec Alamofire> = 4.0 / Alamofire> = 5.0


response.response?.statusCode

Exemple plus détaillé:

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 contient un tout nouveau système d'erreur, regardez ici pour plus de détails)

Pour les utilisateurs de Swift 2.x avec 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
la source
response.result.error peut vous donner une erreur Alamofire, par exemple. StatusCodeValidationFailed, FAILURE: Error Domain=com.alamofire.error Code=-6003. C'est si vous voulez réellement obtenir une erreur de réponse HTTP, il vaut mieux que l'utilisateurresponse.response?.statusCode
Kostiantyn Koval
@KostiantynKoval Je suis d'accord avec vous, vous avez fait une clarification appropriée. J'ai mis à jour ma réponse pour plus de clarté
Alessandro Ornano
50

Dans le gestionnaire d'achèvement avec l'argument responseci-dessous, je trouve que le code d'état http est dans 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
la source
Salut, statusCode 200 tombera-t-il en échec? ma demande a été traitée correctement côté serveur mais la réponse tombe sous Failure
perwyl
1
@perwyl Je ne pense pas que 200 est une erreur http: voir stackoverflow.com/questions/27921537/…
wcochran
2
Le code d'état @perwyl 200 indique le succès, si votre serveur a renvoyé 200 et la réponse indiquant une erreur (c'est-à-dire une propriété appelée issuccess = false), vous devez gérer cela dans votre code frontend
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
la source
Cela stimule les meilleures pratiques de l'API
CESCO
URW :) Essayez d'implémenter Router pour vos requêtes. ;)
Abo3atef
11

Meilleur moyen d'obtenir le code d'état en utilisant alamofire.

 Alamofire.request (URL) .responseJSON {
  réponse dans

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

}
jamesdelacruz
la source
5

Dans votre responseJSONcomplétion, vous pouvez obtenir le code d'état de l'objet de réponse, qui a un type de NSHTTPURLResponse?:

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

Cela fonctionnera indépendamment du fait que le code d'état soit dans la plage d'erreur. Pour plus d'informations, consultez la documentation NSHTTPURLResponse .

Pour votre autre question, vous pouvez utiliser la responseStringfonction pour obtenir le corps de la réponse brute. Vous pouvez ajouter ceci en plus de responseJSONet les deux seront appelés.

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

Votre erreur indique que l'opération est annulée pour une raison quelconque. J'aurais besoin de plus de détails pour comprendre pourquoi. Mais je pense que le plus gros problème peut être que, puisque votre point de terminaison https://host.com/a/pathest faux, il n'y a pas de véritable réponse du serveur à signaler, et par conséquent, vous voyez nil.

Si vous atteignez un point de terminaison valide qui fournit une réponse correcte, vous devriez voir une valeur non nulle pour res(en utilisant les techniques mentionnées par Sam) sous la forme d'un NSURLHTTPResponseobjet avec des propriétés telles que statusCode, etc.

Aussi, juste pour être clair, errorest de type NSError. Il vous indique pourquoi la demande réseau a échoué. Le code d'état de l'échec côté serveur fait en fait partie de la réponse.

J'espère que cela aidera à répondre à votre question principale.

pixels de pluie
la source
Bonne prise, j'étais trop concentré sur la liste de questions en bas.
Sam
1
Il reçoit en fait un code 401 Unauthorized parce que j'envoie intentionnellement le mauvais mot de passe pour simuler un utilisateur en train de saisir son mot de passe afin que je puisse attraper ce cas et donner des commentaires à l'utilisateur. Ce n'est pas l'URL que j'utilise, mais j'utilise une URL légitime qui aboutit au succès lorsque j'envoie le mot de passe correct. La sortie de la console dans mon message d'origine est la sortie réelle de la frappe d'une URL réelle (sauf que l'URL est fausse) et vous pouvez voir que l' resobjet est nil, donc cette réponse n'aide pas, désolé.
GregT
Merci de clarifier. Eh bien, la seule chose suspecte ici est l'erreur -999 que je n'ai pas rencontrée, mais la documentation suggère que la demande est annulée (donc la question de même recevoir une réponse devrait être sans objet). Avez-vous essayé d'imprimer l'objet de réponse pour un type d'erreur différent qui n'est pas lié à l'authentification? Juste curieux.
rainypixels
ahhhh je pensais faire errorréférence à des réponses avec des codes de statut qui sont hors de la fourchette que nous prévoyons validate(). Merci!!!
Gerald
3

Ou utilisez la correspondance de motifs

if let error = response.result.error as? AFError {
   if case .responseValidationFailed(.unacceptableStatusCode(let code)) = error {
       print(code)
   }
}
mbryzinski
la source
A fonctionné comme un charme.
alasker le
3

vous pouvez vérifier le code suivant pour le gestionnaire de code d'état par 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 le code d'état n'est pas validé, il entrera l'échec en cas de commutation

Amr en colère
la source
1

Pour les utilisateurs de Swift 2.0 avec Alamofire> 2.0

Alamofire.request(.GET, url)
  .responseString { _, response, result in
    if response?.statusCode == 200{
      //Do something with result
    }
}
Gerrit Post
la source
1

J'avais besoin de savoir comment obtenir le numéro de code d'erreur réel.

J'ai hérité d'un projet de quelqu'un d'autre et j'ai dû récupérer les codes d'erreur d'une .catchclause qu'ils avaient précédemment configurée pour 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)
}

Ou si vous avez besoin de l'obtenir à partir de la responsevaleur, suivez la réponse 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
la source