【问题标题】:How to handle PHP errors in Swift如何在 Swift 中处理 PHP 错误
【发布时间】:2015-02-06 12:57:48
【问题描述】:

我正在使用 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();
}} ?>

如果密码错误,则系统输出“Failed to connect to DB”

在我的程序中,我有一些事情要处理错误,但我被困在一个部分。

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!

如果我没有正确的密码,但我的 MYSQL 数据库的 IP 地址和端口/用户正确,我的程序就会崩溃。

它崩溃了:

fatal error: unexpectedly found nil while unwrapping an Optional value

并指向 jsonResult。这是有道理的,因为我没有检索两个字符串。

我的问题是,如果我的密码关闭,那么我的 PHP 文件会回显一个字符串。如何搜索该字符串,以便我可以使用 if 语句并阻止我的应用程序崩溃?

【问题讨论】:

    标签: php mysql swift error-handling nsjsonserialization


    【解决方案1】:

    有两个问题。一个是 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);
      
      ?>
      

      注意,这个:

      • Content-Type指定application/json标头;

      • 总是返回一个字典,包含

        • “成功”键,为真或假;

        • 如果出现错误,error_code 指示错误类型(1 = 未提供密码;2 = 连接失败;3 = 未找到数据库;4 = 一些 SQL 错误);

          李>
        • 如果有错误,一个error_msg字符串表示错误信息字符串;和

        • 如果成功,则为 results 数组(很像您过去在根级别返回)。

    2. 在 Swift 方面,您需要:

      • 更改它以查找这些各种服务器应用程序级错误(注意,我将顶级结构设为字典,而您的原始字典数组具有特定值;

      • 1234563 .);
    3. 您可能还想检查 JSON 解析错误(以防服务器中的某些错误阻止返回格式正确的 JSON);和

    4. 您确实应该对密码进行百分比转义(因为如果它包含+&amp; 字符,否则将无法成功传输)。

    5. 因此,您可能会遇到以下情况:

      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】:

      您的问题可能出在这一行(为清楚起见已包装):

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

      当您的 PHP 脚本仅通过返回字符串来报告错误时,它返回了无效的 JSON。当您使用 NSJSONSerialization.JSONObjectWithData 解析它时,如果 JSON 无效,该方法将返回 nil,就像您的一样。

      然后,您获取该值并将其分配给您已声明不是可选的 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是否为nil,如果是,你就知道发生了错误,并且可以返回查看data对象看看它是什么。

      不过,即便如此,也会给您带来麻烦。 JSON 文档的根不必是字典;它可能是一个数组。而且即使是字典,值也不一定都是字符串;它们可以是数字、布尔值或嵌套数组或字典!

      Objective-C 相对宽松的类型检查使得这很容易处理,但 Swift 更严格。最好使用 Swift 特定的JSONlibraries 之一。这将使您的代码更加健壮。

      祝你好运!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-21
        • 1970-01-01
        • 2016-12-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多