Swift Alamofire: como obter o código de status de resposta HTTP

106

Eu gostaria de recuperar o código de status da resposta HTTP (por exemplo, 400, 401, 403, 503, etc) para falhas de solicitação (e de preferência para sucessos também). Neste código, estou executando a autenticação do usuário com HTTP Basic e quero ser capaz de enviar uma mensagem ao usuário que a autenticação falhou quando o usuário digita incorretamente a senha.

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

Infelizmente, o erro produzido não parece indicar que um código de status HTTP 409 foi realmente recebido:

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

Além disso, seria bom recuperar o corpo HTTP quando ocorrer um erro, porque meu lado do servidor colocará uma descrição textual do erro lá.

Perguntas
É possível recuperar o código de status após uma resposta não 2xx?
É possível recuperar o código de status específico após uma resposta 2xx?
É possível recuperar o corpo HTTP após uma resposta não 2xx?

Obrigado!

GregT
fonte
1
Se você não estiver autenticado, receberá um -999 por design. Não sei por que isso acontece ou como pode ser resolvido ... Você resolveu isso?
Guido Hendriks de

Respostas:

187

Para usuários Swift 3.x / Swift 4.0 / Swift 5.0 com Alamofire> = 4.0 / Alamofire> = 5.0


response.response?.statusCode

Exemplo mais detalhado:

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 contém um sistema de erro completamente novo, veja aqui para detalhes)

Para usuários Swift 2.x com 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
fonte
response.result.error pode dar a você um erro Alamofire, por exemplo. StatusCodeValidationFailed, FAILURE: Error Domain=com.alamofire.error Code=-6003. Se você realmente deseja obter um erro de resposta HTTP, é melhor para o usuárioresponse.response?.statusCode
Kostiantyn Koval
@KostiantynKoval Eu concordo com você, você fez um bom esclarecimento. Atualizei minha resposta para maior clareza
Alessandro Ornano
50

No manipulador de conclusão com o argumento responseabaixo, acho que o código de status http está em 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
fonte
Olá, o statusCode 200 falhará? minha solicitação foi tratada corretamente no lado do servidor, mas a resposta cai em Falha
perwyl
1
@perwyl Não acho que 200 seja um erro http: veja stackoverflow.com/questions/27921537/…
wcochran
2
O código de status @perwyl 200 está indicando sucesso, se o seu servidor retornou 200 e a resposta indicando erro (ou seja, alguma propriedade chamada issuccess = false), então você terá que lidar com isso em seu código de 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
fonte
Isso estimula as melhores práticas da API
CESCO
URW :) Tente implementar um roteador para suas solicitações. ;)
Abo3atef
11

A melhor maneira de obter o código de status usando alamofire.

 Alamofire.request (URL) .responseJSON {
  resposta em

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

}
Jamesdelacruz
fonte
5

Ao responseJSONconcluir, você pode obter o código de status do objeto de resposta, que tem um tipo de NSHTTPURLResponse?:

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

Isso funcionará independentemente de o código de status estar na faixa de erro. Para obter mais informações, dê uma olhada na documentação NSHTTPURLResponse .

Para sua outra pergunta, você pode usar a responseStringfunção para obter o corpo da resposta bruta. Você pode adicionar isto além de responseJSONe ambos serão chamados.

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

Seu erro indica que a operação está sendo cancelada por algum motivo. Precisaria de mais detalhes para entender o porquê. Mas acho que o maior problema pode ser que, como seu endpoint https://host.com/a/pathé falso, não há uma resposta real do servidor para relatar e, portanto, você está vendo nil.

Se você atingir um endpoint válido que forneça uma resposta adequada, deverá ver um valor não nulo para res(usando as técnicas mencionadas por Sam) na forma de um NSURLHTTPResponseobjeto com propriedades como statusCodeetc.

Além disso, só para ficar claro, erroré do tipo NSError. Ele informa por que a solicitação de rede falhou. O código de status da falha no lado do servidor é, na verdade, uma parte da resposta.

Espero que ajude a responder sua pergunta principal.

rainypixels
fonte
Boa captura, eu estava muito focado na lista de perguntas no final.
Sam
1
Na verdade, está recebendo um código 401 Não autorizado porque estou enviando intencionalmente a senha errada para simular um usuário digitando incorretamente a senha para que eu possa pegar esse caso e dar um feedback ao usuário. Essa não é a URL real que estou usando, mas estou usando uma URL legítima que resulta em sucesso quando envio a senha correta. A saída do console em minha postagem original é a saída real de acessar uma URL real (exceto que a URL é falsa) e você pode ver que o resobjeto é nil, então esta resposta não ajuda, desculpe.
GregT
Obrigado por esclarecer. Bem, então a única coisa suspeita aqui é o erro -999 que não encontrei, mas a documentação sugere que a solicitação está sendo cancelada (portanto, a questão de até mesmo receber uma resposta deve ser discutível). Você tentou imprimir o objeto de resposta para um tipo diferente de erro não relacionado à autenticação? Apenas curioso.
rainypixels de
ahhhh achei que errorestivesse me referindo a respostas com códigos de status que estão fora do intervalo que fornecemos validate(). Obrigado!!!
Gerald
3

Ou use correspondência de padrões

if let error = response.result.error as? AFError {
   if case .responseValidationFailed(.unacceptableStatusCode(let code)) = error {
       print(code)
   }
}
Mbryzinski
fonte
Funcionou como um encanto.
Alaska
3

você pode verificar o seguinte código para manipulador de código de status por 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)
        }
    }

se o código de status não for validado, será inserido a falha no caso de troca

Amr irritado
fonte
1

Para usuários Swift 2.0 com Alamofire> 2.0

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

Eu precisava saber como obter o número do código de erro real.

Eu herdei um projeto de outra pessoa e tive que obter os códigos de erro de uma .catchcláusula que eles haviam configurado anteriormente 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)
}

Ou se você precisar obtê-lo do responsevalor siga a resposta 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
fonte