Как обрабатывать ошибки PHP в Swift

Я использую PHP & JSON для извлечения некоторых данных из базы данных.

Это мой PHP файл

<?php
error_reporting(0);
ini_set('error_reporting', E_ALL);
ini_set('display_errors','Off');
$mysqli = new mysqli("localhost", "root", $_REQUEST['password'], "");
if ($mysqli->connect_errno) {
echo "Failed to connect to DB.";
die();
} else {
$dbs = array();
$res = $mysqli->query("SHOW DATABASES");
$res->data_seek(0);
if ($res->num_rows > 0) {
while($row = $res->fetch_assoc()) {
$dbs[] = $row;
}
echo json_encode($dbs);
} else {
echo "Failed to get list of databases from server.";
die();
}} ?>

Если пароль неверный, система выдает «Не удалось подключиться к БД»

В моей программе есть вещи для обработки ошибок, но я застрял в одной части.

let urlString = "http://\(hostTextField.text):\(portTextField.text)/dblist.php?    &password=\(passTextField.text)"let url: NSURL  = NSURL(string: urlString)!
let urlSession = NSURLSession.sharedSession()

println(url)
println(urlSession)

//2
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in

println(response)
println(data)

if (error != nil) {

println("Can't connect using credentials")

dispatch_async(dispatch_get_main_queue(), {

HUDController.sharedController.hide(afterDelay: 0.1)

})

sleep(1)
var refreshAlert = UIAlertController(title: "Camaleon Reports", message: "Can't connect to the database", preferredStyle: UIAlertControllerStyle.Alert)
refreshAlert.addAction(UIAlertAction(title: "Retry", style: .Default, handler: { (action: UIAlertAction!) in
println("Yes Logic")

}))
self.presentViewController(refreshAlert, animated: true, completion: nil)
return }
var err: NSError?
var jsonResult: [Dictionary<String, String>] = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as [Dictionary<String, String>]
// 3
if (err != nil) {

println("Still cant connect....")
println("JSON Error \(err!.localizedDescription)")
}

var jsonDB : [Dictionary<String, String>] = jsonResult
for currentDictionary in jsonDB{
var currentEntry = currentDictionary["Database"] as String!

Моя программа падает, если у меня нет правильного пароля, но есть правильный IP-адрес и порт / пользователь для базы данных MYSQL.

Вылетает с этим:

fatal error: unexpectedly found nil while unwrapping an Optional value

и указывает на JsonResult. Это имеет смысл, потому что я не получаю две строки.

Моя проблема в том, что если мой пароль отключен, то мой PHP-файл отображает строку. Как я могу найти эту строку, чтобы использовать оператор if и предотвратить сбой приложения?

3

Решение

Ваша проблема, вероятно, в этой строке (обернуто для ясности):

var jsonResult: [Dictionary<String, String>] =
NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.MutableContainers,
error: &err) as [Dictionary<String, String>]

Когда ваш PHP-скрипт сообщает об ошибке, просто возвращая строку, он возвращает неверный JSON. Когда вы используете NSJSONSerialization.JSONObjectWithData чтобы разобрать, этот метод вернет nil если JSON недействителен, как ваш.

Затем вы берете это значение и назначаете его объявленной вами переменной Swift, которая не является необязательной. Попытка назначить nil переменной, не объявленной ни ? или же ! это ошибка во время выполнения в Swift. (Вы не получаете ошибку во время компиляции, потому что вы используете as бросить значение.)

Один из способов исправить это — изменить ваш PHP, чтобы ошибка соответствовала JSON:

echo "{ \"error\": \"Failed to connect to DB.\" }"; # or something, my PHP is rusty

Но это все еще оставляет вашу программу Swift в хрупком состоянии; получение чего-либо, кроме правильного JSON с сервера, приведет к сбою.

Лучше объявить jsonResult переменная как необязательная:

var jsonResult: [Dictionary<String, String>]? =
NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.MutableContainers,
error: &err) as [Dictionary<String, String>]?

Тогда в вашем коде вы можете явно проверить, jsonResult это ноль, и если это так, вы знаете, что произошла ошибка, и можете вернуться и посмотреть на data объект, чтобы увидеть, что это было.

Даже это, тем не менее, может оставить вас в беде. Корень документа JSON не обязательно должен быть словарем; это может быть массив. И даже если это словарь, значения могут не быть строками; это могут быть числа, логические значения, вложенные массивы или словари!

Относительно слабая проверка типов в Objective-C позволяет легко с этим справляться, но Swift более строг. Лучше всего использовать один из специфичных для Swift JSON библиотеки. Это сделает ваш код гораздо более надежным.

Удачи!

2

Другие решения

Есть два вопроса. Один из них — PHP, а второй — Swift.

  1. Ваш PHP действительно никогда не должен просто сообщать об ошибке. Я бы предложил, чтобы он всегда возвращал JSON. Это облегчит для вашего клиентского кода правильное обнаружение и обработку ошибок.

    <?php
    
    header("Content-Type: application/json");
    
    $response = array();
    
    error_reporting(0);
    ini_set('error_reporting', E_ALL);
    ini_set('display_errors','Off');
    if (!isset($_REQUEST['password'])) {
    $response["success"] = false;
    $response["error_code"] = 1;
    $response["error_message"] = "No password provided";
    echo json_encode($response);
    exit();
    }
    
    $mysqli = new mysqli("localhost", "root", $_REQUEST['password'], "");
    if ($mysqli->connect_errno) {
    $response["success"] = false;
    $response["error_code"] = 2;
    $response["mysql_error_code"] = $mysqli->connect_errno;
    $response["error_message"] = $mysqli->connect_error;
    echo json_encode($response);
    exit();
    }
    
    if ($res = $mysqli->query("SHOW DATABASES")) {
    $dbs = array();
    $res->data_seek(0);
    if ($res->num_rows > 0) {
    while($row = $res->fetch_assoc()) {
    $dbs[] = $row;
    }
    $response["success"] = true;
    $response["results"] = $dbs;
    } else {
    $response["success"] = false;
    $response["error_code"] = 3;
    $response["error_message"] = "Failed to get list of databases from server.";
    }
    
    $res->close();
    } else {
    $response["success"] = false;
    $response["error_code"] = 4;
    $response["mysql_error_code"] = $mysqli->errno;
    $response["error_message"] = $mysqli->error;
    }
    $mysqli->close();
    
    echo json_encode($response);
    
    ?>
    

    Обратите внимание, это:

    • Определяет application/json заголовок для Content-Type;

    • Всегда возвращает словарь, содержащий

      • ключ «успеха», который является истинным или ложным;

      • если ошибка, error_code указание типа ошибки (1 = пароль не указан; 2 = ошибка подключения; 3 = базы данных не найдены; 4 = ошибка SQL);

      • если ошибка, error_msg строка, указывающая строку сообщения об ошибке; а также

      • если успех, results массив (так же, как вы использовали для возврата на корневом уровне).

  2. Со стороны Swift вам необходимо:

    • Измените его, чтобы искать эти различные ошибки уровня приложения сервера (обратите внимание, я делаю структуру верхнего уровня словарем, а ваш исходный массив словарей — конкретным значением;

    • Вы можете заранее проверить statusCode из response объект, чтобы убедиться, что сервер дал вам 200 код возврата (например, 404 означает, что страница не найдена и т. д.);

    • Возможно, вы также захотите проверить наличие ошибок синтаксического анализа JSON (в случае, если какая-то ошибка на сервере помешала вернуть правильно сформированный JSON); а также

    • Вы действительно должны быть в процентах, экранируя пароль (потому что, если он включен + или же & символы, это не будет передано успешно в противном случае).

    Таким образом, у вас может быть что-то вроде:

    let encodedPassword = password.stringByAddingPercentEncodingForURLQueryValue()!
    let body = "password=\(encodedPassword)"let request = NSMutableURLRequest(URL: URL!)
    request.HTTPBody = body.dataUsingEncoding(NSUTF8StringEncoding)!
    request.HTTPMethod = "POST"
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
    
    // detect fundamental network error
    
    guard error == nil && data != nil else {
    print("network error: \(error)")
    return
    }
    
    // detect fundamental server errors
    
    if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode != 200 {
    // some server error
    print("status code was \(httpResponse.statusCode); not 200")
    return
    }
    
    // detect parsing errors
    
    guard let responseObject = try? NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String : AnyObject] else {
    // some problem parsing the JSON response
    print(String(data: data!, encoding: NSUTF8StringEncoding))
    return
    }
    
    // now parse app-level response to make sure `status` was `true`
    
    guard let success = responseObject!["success"] as? NSNumber else {
    print("Problem extracting the `success` value") // we should never get here
    return
    }
    
    if !success.boolValue {
    print("server reported error")
    if let errorCode = responseObject!["error_code"] as? NSNumber {
    switch (errorCode.integerValue) {
    case 1:
    print("No password provided")
    case 2:
    print("Connection failed; probably bad password")
    case 3:
    print("No databases found")
    case 4:
    print("Some SQL error")
    default:
    print("Unknown error code: \(errorCode)") // should never get here
    }
    }
    if let errorMessage = responseObject!["error_message"] as? String {
    print("  message=\(errorMessage)")
    }
    return
    }
    
    if let databases = responseObject!["results"] as? [[String : AnyObject]] {
    print("databases = \(databases)")
    }
    }
    task.resume()
    

    Процентный код находится в String категория:

    extension String {
    
    // see RFC 3986
    
    func stringByAddingPercentEncodingForURLQueryValue() -> String? {
    let characterSet = NSCharacterSet(charactersInString:"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
    return self.stringByAddingPercentEncodingWithAllowedCharacters(characterSet)
    }
    }
    

Пара других вспомогательных наблюдений:

  1. Никогда не отправляйте пароли в открытом виде. Поместите их в тело POST запрос (не URL), а затем используйте https URL.

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

2

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector