Обработка ошибок в Alamofire

У меня есть код HTTP в контроллере AngularJS:

$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 = "";
    });

В случае успеха сервер будет отвечать представлением пользователя JSON. В случае ошибки сервер ответит простой строкой, такой как User not found, к которой можно получить доступ через параметр data.

Мне сложно понять, как сделать что-то подобное в Alamofire. Вот что я имею прямо сейчас:

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

Я понятия не имею, правильно ли я правильно обрабатываю случай без ошибок, и также хотел бы получить информацию об этом.

Ответ 1

Вы находитесь на правильном пути, но вы столкнетесь с некоторыми важными проблемами с вашей текущей реализацией. Есть некоторые вещи Alamofire низкого уровня, которые собираются вас оттуда, и я хочу помочь вам. Вот альтернативная версия вашего кода, которая будет намного эффективнее.

@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

Во-первых, функция validate в запросе будет проверять следующее:

  • HTTPStatusCode - должно быть 200... 299
  • Content-Type - этот заголовок в ответе должен соответствовать заголовку Accept в исходном запросе

Более подробную информацию о проверке в Alamofire можно найти в README.

Ослабить/усилить

Удостоверьтесь в слабом самости и сильном самозакрытии, чтобы убедиться, что вы не создаете цикл удержания.

Отправка на главную очередь

Отправка вызова в основную очередь не требуется. Alamofire гарантирует, что ваш обработчик завершения в сериализаторах response и responseJSON вызывается в основной очереди уже. Фактически вы можете предоставить свою собственную очередь отправки для запуска сериализаторов, если хотите, но ни ваше решение, ни мое в настоящее время не делают так, что вызовы диспетчеризации в основную очередь совершенно не нужны.

Сериализатор ответов

В вашем конкретном случае вы фактически не хотите использовать сериализатор responseJSON. Если вы это сделаете, вы не сможете вернуть данные, если вы не пройдете проверку. Причина в том, что ответ от сериализации JSON - это то, что будет возвращено как AnyObject. Если сбой сериализации, AnyObject будет равен нулю, и вы не сможете прочитать данные.

Вместо этого используйте сериализатор response и попробуйте вручную проанализировать данные с помощью NSJSONSerialization. Если это не удается, вы можете использовать метод ole NSString(data:encoding:) для печати данных.

Надеюсь, это поможет пролить свет на некоторые довольно сложные способы, чтобы сработаться.

Ответ 2

Таким образом, Alamofire обрабатывает все запросы успешно. Это действительно сводится к возврату HTTP-заголовков API-сервера.

Вы можете использовать Alamofire.Request.validate()

Это позволит вам проверять заголовки http и т.д. Проверьте пример

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

Я предполагаю, что сообщение об ошибке будет в объекте data.

для доступа к значениям из данных вы можете сделать что-то вроде

Я не совсем уверен в вашем ответе api, но в этом примере

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