Swift Alamofire: Jak uzyskać kod stanu odpowiedzi HTTP

106

Chciałbym pobrać kod stanu odpowiedzi HTTP (np. 400, 401, 403, 503 itd.) W przypadku niepowodzeń żądań (a najlepiej również w przypadku sukcesów). W tym kodzie przeprowadzam autoryzację użytkownika za pomocą HTTP Basic i chcę mieć możliwość przesłania użytkownikowi wiadomości, że uwierzytelnianie nie powiodło się, gdy użytkownik błędnie wpisuje hasło.

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

Niestety, wygenerowany błąd nie wydaje się wskazywać, że faktycznie odebrano kod stanu 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

Ponadto byłoby miło pobrać treść HTTP, gdy wystąpi błąd, ponieważ mój serwer umieści tam tekstowy opis błędu.

Pytania
Czy można pobrać kod statusu w odpowiedzi innej niż 2xx?
Czy można odzyskać określony kod statusu po odpowiedzi 2xx?
Czy można pobrać treść HTTP na podstawie odpowiedzi innej niż 2xx?

Dzięki!

GregT
źródło
1
Jeśli nie jesteś uwierzytelniony, otrzymasz projekt -999. Nie wiem, dlaczego tak jest i jak można to rozwiązać ... Czy rozwiązałeś to?
Guido Hendriks,

Odpowiedzi:

187

Dla użytkowników Swift 3.x / Swift 4.0 / Swift 5.0 z Alamofire> = 4.0 / Alamofire> = 5.0


response.response?.statusCode

Bardziej szczegółowy przykład:

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 zawiera zupełnie nowy system błędów, spójrz tutaj po szczegóły)

Dla użytkowników Swift 2.x z 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
źródło
response.result.error może na przykład spowodować błąd Alamofire. StatusCodeValidationFailed, FAILURE: Error Domain=com.alamofire.error Code=-6003. Jeśli faktycznie chcesz uzyskać błąd odpowiedzi HTTP, lepiej dla użytkownikaresponse.response?.statusCode
Kostiantyn Koval
@KostiantynKoval Zgadzam się z tobą, dokonałeś właściwego wyjaśnienia. Zaktualizowałem moją odpowiedź dla jasności
Alessandro Ornano
50

W module obsługi zakończenia z argumentem responseponiżej znajduję kod statusu http 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
źródło
Cześć, czy statusCode 200 ulegnie awarii? moja prośba została poprawnie obsłużona po stronie serwera, ale odpowiedź mieści się w kategorii Failure
perwyl
1
@perwyl Nie sądzę, że 200 to błąd http: patrz stackoverflow.com/questions/27921537/ ...
wcochran
2
Kod statusu @perwyl 200 wskazuje na sukces, jeśli twój serwer zwrócił 200, a odpowiedź wskazała błąd (np. jakaś właściwość o nazwie issuccess = false), to musisz sobie z tym poradzić w swoim kodzie frontendowym
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
źródło
To stymuluje najlepsze praktyki API
CESCO
URW :) Spróbuj zaimplementować router do swoich żądań. ;)
Abo3atef
11

Najlepszy sposób na uzyskanie kodu statusu za pomocą alamofire.

 Alamofire.request (URL) .responseJSON {
  odpowiedź w

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

}
jamesdelacruz
źródło
4

W swoim responseJSONzakończeniu możesz pobrać kod statusu z obiektu odpowiedzi, który ma typ NSHTTPURLResponse?:

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

Będzie to działać niezależnie od tego, czy kod stanu mieści się w zakresie błędu. Aby uzyskać więcej informacji, zapoznaj się z dokumentacją NSHTTPURLResponse .

W przypadku drugiego pytania możesz użyć responseStringfunkcji, aby uzyskać nieprzetworzoną treść odpowiedzi. Możesz dodać to dodatkowo do responseJSONi oba zostaną wywołane.

.responseJson { (req, res, json, error) in
   // existing code
}
.responseString { (_, _, body, _) in
   // body is a String? containing the response body
}
Sam
źródło
3

Twój błąd wskazuje, że operacja jest z jakiegoś powodu anulowana. Potrzebuję więcej szczegółów, żeby zrozumieć, dlaczego. Ale myślę, że większym problemem może być to, że ponieważ twój punkt końcowy https://host.com/a/pathjest fałszywy, nie ma prawdziwej odpowiedzi serwera na raport, a więc widzisz nil.

Jeśli trafisz na prawidłowy punkt końcowy, który podaje właściwą odpowiedź, powinieneś zobaczyć niezerową wartość dla res(używając technik, o których wspomina Sam) w postaci NSURLHTTPResponseobiektu o właściwościach takich jak statusCodeitp.

Poza tym, żeby było jasne, errorjest typowy NSError. Informuje, dlaczego żądanie sieciowe nie powiodło się. Kod stanu błędu po stronie serwera jest w rzeczywistości częścią odpowiedzi.

Mam nadzieję, że pomoże to odpowiedzieć na twoje główne pytanie.

rainypixels
źródło
Dobry chwyt, byłem zbyt skupiony na liście pytań na dole.
Sam
1
W rzeczywistości otrzymuję kod 401 nieautoryzowany, ponieważ celowo wysyłam nieprawidłowe hasło, aby zasymulować użytkownika wpisującego hasło, aby móc złapać ten przypadek i przekazać mu opinię. To nie jest rzeczywisty adres URL, którego używam, ale używam legalnego adresu URL, który kończy się sukcesem, gdy wyślę prawidłowe hasło. Wynik konsoli w moim oryginalnym poście to rzeczywisty wynik uderzenia w prawdziwy adres URL (z wyjątkiem tego, że adres URL jest fałszywy) i możesz zobaczyć, że resobiekt jest nil, więc ta odpowiedź nie pomaga, przepraszam.
GregT
Dzięki za wytłumaczenie. Cóż, jedyną rzeczą, która jest tutaj podejrzana, jest błąd -999, którego nie napotkałem, ale dokumentacja sugeruje, że żądanie jest anulowane (więc kwestia nawet otrzymania odpowiedzi powinna być dyskusyjna). Czy próbowałeś wydrukować obiekt odpowiedzi dla innego typu błędu, który nie jest związany z uwierzytelnianiem? Po prostu ciekawy.
rainypixels
ahhhh, myślałem, że errorodnosi się do odpowiedzi z kodami stanu, które są poza zakresem, który przewidujemy validate(). Dzięki!!!
Gerald
3

Lub użyj dopasowania do wzorca

if let error = response.result.error as? AFError {
   if case .responseValidationFailed(.unacceptableStatusCode(let code)) = error {
       print(code)
   }
}
mbryzinski
źródło
Działał jak urok.
alasker,
3

możesz sprawdzić następujący kod programu obsługi kodu statusu przez 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)
        }
    }

jeśli kod statusu nie zostanie zatwierdzony, zostanie wprowadzony błąd w przypadku przełącznika

Amr Angry
źródło
1

Dla użytkowników Swift 2.0 z Alamofire> 2.0

Alamofire.request(.GET, url)
  .responseString { _, response, result in
    if response?.statusCode == 200{
      //Do something with result
    }
}
Gerrit Post
źródło
1

Musiałem wiedzieć, jak uzyskać rzeczywisty numer kodu błędu.

Odziedziczyłem projekt po kimś innym i musiałem uzyskać kody błędów z .catchklauzuli, którą wcześniej skonfigurowali dla 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)
}

Lub jeśli chcesz uzyskać to z responsewartości, postępuj zgodnie z odpowiedzią @ 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
źródło