2015-03-13 22 views
12

mam kod HTTP w angularjs Kontroler:Obsługa błędów w Alamofire

$http.post('/api/users/authenticate', {email: $scope.email, password: $scope.password}) 
    .success(function (data, status, headers, config) { 
     authService.login($scope.email); 
     $state.go('home'); 
    }) 
    .error(function (data, status, headers, config) { 
     $scope.errorMessages = data; 
     $scope.password = ""; 
    }); 

W przypadku sukcesu, serwer odpowie reprezentacji JSON użytkownika. W przypadku błędu serwer odpowie prostym łańcuchem, takim jak User not found, do którego można uzyskać dostęp za pomocą parametru data.

Mam problem z ustaleniem, jak zrobić coś podobnego w Alamofire. Oto co mam teraz:

@IBAction func LoginPressed(sender: AnyObject) { 
    let params: Dictionary<String,AnyObject> = ["email": emailField.text, "password": passwordField.text] 

    Alamofire.request(.POST, "http://localhost:3000/api/users/authenticate", parameters: params) 
     .responseJSON {(request, response, data, error) in 
      if error == nil { 
       dispatch_async(dispatch_get_main_queue(), { 
        let welcome = self.storyboard?.instantiateViewControllerWithIdentifier("login") as UINavigationController; 

        self.presentViewController(welcome, animated: true, completion: nil); 
       }) 
      } 
      else{ 
       dispatch_async(dispatch_get_main_queue(), { 
        // I want to set the error label to the simple message which I know the server will return 
        self.errorLabel.text = "something went wrong" 
       }); 
      } 
    } 
} 

nie mam pojęcia, czy mam prowadzącej sprawę bez błędu prawidłowo albo i ceniłaby wejście na tym również.

Odpowiedz

26

Jesteś są na dobrej drodze, ale ty napotkasz kilka istotnych problemów z twoją obecną implementacją. Jest kilka rzeczy o niskim poziomie Alamofire, które cię potkną, że chcę ci pomóc. Oto alternatywna wersja próbki kodu, która będzie znacznie bardziej efektywna.

@IBAction func loginPressed(sender: AnyObject) { 
    let params: [String: AnyObject] = ["email": emailField.text, "password": passwordField.text] 

    let request = Alamofire.request(.POST, "http://localhost:3000/api/users/authenticate", parameters: params) 
    request.validate() 
    request.response { [weak self] request, response, data, error in 
     if let strongSelf = self { 
      let data = data as? NSData 

      if data == nil { 
       println("Why didn't I get any data back?") 
       strongSelf.errorLabel.text = "something went wrong" 
       return 
      } else if let error = error { 
       let resultText = NSString(data: data!, encoding: NSUTF8StringEncoding) 
       println(resultText) 
       strongSelf.errorLabel.text = "something went wrong" 
       return 
      } 

      var serializationError: NSError? 

      if let json: AnyObject = NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments, error: &serializationError) { 
       println("JSON: \(json)") 
       let welcome = self.storyboard?.instantiateViewControllerWithIdentifier("login") as UINavigationController 
       self.presentViewController(welcome, animated: true, completion: nil) 
      } else { 
       println("Failed to serialize json: \(serializationError)") 
      } 
     } 
    } 
} 

Validation

Po pierwsze, funkcja validate na życzenie zatwierdzanie następujących kategorii:

  • HTTPStatusCode - Musi być 200 ... 299
  • Content-Type - Ta nagłówek w odpowiedzi musi odpowiadać nagłówkowi Accept w pierwotnym żądaniu

Możesz znaleźć więcej informacji o walidacji w Alamofire w README.

Weakify/Strongify

Upewnij się słabym siebie i swoją silną siebie zamknięcia, aby upewnić się, że nie kończy się tworząc zachować cykl.

Wysłanie do głównej kolejki

swojej wysłania oddzwania do głównej kolejki nie są konieczne. Alamofire gwarantuje, że twój program obsługi zakończeń w serializerach response i responseJSON jest już wywoływany w głównej kolejce. W rzeczywistości możesz podać swoją własną kolejkę wywołania, aby uruchomić seralizatory, ale żadne z twoich rozwiązań ani tego nie powoduje, że wywołanie wysyłki do głównej kolejki jest zupełnie niepotrzebne.

serializer Response

w danym przypadku, w rzeczywistości nie chcą używać responseJSON serializatora. Jeśli to zrobisz, nie otrzymasz żadnych danych, jeśli nie przejdziesz sprawdzania poprawności. Powodem jest to, że odpowiedź z serializacji JSON zostanie zwrócona jako AnyObject. Jeśli serializacja nie powiedzie się, AnyObject będzie zerowe i nie będzie można odczytać danych.

Zamiast tego należy użyć serializatora response i spróbować przeanalizować dane ręcznie za pomocą NSJSONSerialization. Jeśli to zawiedzie, możesz polegać na dobrej metodzie ole NSString(data:encoding:), aby wydrukować dane.

Mamy nadzieję, że pomoże to rzucić nieco światła na dość skomplikowane sposoby, aby się potknąć.

+0

Czy użycie [słabego siebie] jest konieczne, gdy nie jest przywoływane? Zawsze rozumiałem, że nie jest to konieczne, jeśli zamknięcie nie jest nigdzie mocno przywoływane. – chourobin

+4

Świetne pytanie, niemożliwe do udzielenia zwięzłej odpowiedzi. To zależy od zachowania, które chcesz. Jeśli chcesz, aby właściciel metody 'loginPressed' był przechowywany w pamięci, bez względu na to, czy nic innego nie ma na ten temat odniesienia, nie używałbyś' [słabego self] '. Zawsze jednak istnieje potencjalne ryzyko utworzenia cyklu zatrzymania, jeśli nie używasz wzoru osłabienia/wzmocnienia. Więcej informacji [tutaj] (http://blackpixel.com/blog/2014/03/capturing-myself.html) i [tutaj] (http://stackoverflow.com/questions/24320347/shall-we-always-use -połączone-wewnątrz-zamknięcie-w-szybkim). – cnoon

+0

Dobre wyjaśnienie. W Swift 3 możesz zastąpić 'if let strongSelf = self {...}' z '' guard "strong" self = else else {return} ' – Lion

1

Alamofire traktuje wszystkie żądania pomyślnie. To naprawdę sprowadza się do zwracania nagłówków HTTP serwera API.

Można użyć Alamofire.Request.validate()

To będzie pozwalają sprawdzić poprawność nagłówków HTTP, itd. Sprawdź przykładowe

https://github.com/Alamofire/Alamofire#validation

jestem zakładając komunikat błędu będzie w obiekcie data.

aby uzyskać dostęp do wartości z danymi można zrobić coś takiego

nie jestem pewien o swój wygląd reagowania api, ale w tym przykładzie

{ 
    "message": "Could not authenticate" 
} 
let message: String? = data?.valueForKey("message") as String 
+0

Dobrze, wszystko jest traktowane jako pomyślne. W przypadku błędu kod stanu może wynosić 500 i zakładam, że "błąd" nie będzie mieć wartości NULL. Kiedy tak jest, '' 'data''' jest' 'AnyObject''' i nie mam pewności, jak uzyskać dostęp do rzeczywistej treści' '' User not found'''. –