我正在使用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();
}} ?>
如果密码错误,则系统输出&#34;无法连接到DB&#34;
在我的程序中,我有处理错误的事情,但我被困在一个部分。
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语句并阻止我的应用程序崩溃?
答案 0 :(得分: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的JSON libraries之一。这将使您的代码更加健壮。
祝你好运!答案 1 :(得分:2)
有两个问题。一个是PHP,一个是Swift。
您的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
标头
始终返回包含
的字典a&#34;成功&#34;密钥,无论是真还是假;
如果出现错误,error_code
表示错误类型(1 =未提供密码; 2 =连接失败; 3 =未找到数据库; 4 =某些SQL错误);
如果是错误,则表示错误消息字符串的error_msg
字符串;以及
如果成功,则为results
数组(非常类似于您以前在根级别返回的数据)。
在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)
}
}
其他一些辅助观察:
切勿以明文形式发送密码。将它们放在POST
请求(不是网址)的正文中,然后使用https
网址。
我个人不会使用应用级身份验证的MySQL密码部分。我将MySQL身份验证逻辑编码在服务器端,然后使用您自己的用户身份验证表。