Swift 没有将变量传递给 PHP Web 服务来查询我的 SQL 数据库

Swift not passing variables to PHP Web Service to query my SQL database

我有一个 PHP Web 服务,如果我直接将值硬编码到 SQL 查询中,它可以向我的数据库插入一个新行。但是当我尝试使用从我的 Swift 函数传递给 PHP 脚本的变量来执行此操作时,它会崩溃。我已经在我的 Swift 中添加了打印语句,因此我确信这些值已正确传递给函数,并且错误一定在之后的行中。

PHP:我已经用硬编码值 - Carrots 评论了 SQL 查询行,但这个查询确实有效,所以我认为 PHP->SQL工作正常,但 Swift->PHP 是问题所在。

 <?php
$servername = "localhost";
$username = "*******";
$password = "*****";
$dbname = "*******";

$product_name = $_POST['product_name'] ?? 'DefaultProduct';
$code = $_POST['code'] ?? 'DefaultCode';
$Brands = $_POST['brands'] ?? '1';
$Comp1Mat = $_POST['Comp1Mat'] ?? 'DefaultMat';
$Comp2Mat = $_POST['Comp2Mat'] ?? '1';

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
  die("Connection failed: " . $conn->connect_error);
}

//$sql = "INSERT INTO Products (product_name, code) VALUES ('Carrots', '0135')";

$sql = $conn->prepare("INSERT INTO Products (product_name, code, brands, Comp1Mat, Comp2Mat) VALUES (?, ?, ?, ?, ?)";
$sql->bind_param("isssi", $product_name, $code, $Brands, $Comp1Mat, $Comp2Mat);
$sql->execute();
$sql->close();

if ($conn->query($sql) === TRUE) {
  echo "New record created successfully";
} else {
  echo "Error: " . $sql . "<br>" . $conn->error;
}

$conn->close();
?> 

Swift:

func addProduct(product_name: String, code: String, brands: String, Comp1Mat: String, Comp2Mat: String){
        
        print(product_name)
        print(code)
        print(brands)
        print(Comp1Mat)
        print(Comp2Mat)
        
        let insertProductURL = URL(string: "http://recyclingmadesimple.xyz/insertproduct.php")!
        var urlRequest = URLRequest(url: insertProductURL)
        urlRequest.httpMethod = "POST"
        // Set HTTP Request Headers
        urlRequest.setValue("application/json", forHTTPHeaderField: "Accept");
        urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type");
        
        let postString = "product_name=\(product_name)&code=\(code)&brands=\(brands)&Comp1Mat=\(Comp1Mat)&Comp2Mat=\(Comp2Mat)"
        urlRequest.httpBody = postString.data(using: .utf8)
        
        let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            if error != nil {
                print("error = \(String(describing: error))")
                return
            }
            print("response - \(String(describing: response))")
            let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
            print("response string - \(String(describing: responseString))")
        }
        task.resume()

    }

错误:

response - Optional(<NSHTTPURLResponse: 0x281191600> { URL: http://recyclingmadesimple.xyz/insertproduct.php } { Status Code: 500, Headers {
    Connection =     (
        "Upgrade, close"
    );
    "Content-Length" =     (
        0
    );
    "Content-Type" =     (
        "text/html; charset=UTF-8"
    );
    Date =     (
        "Thu, 17 Jun 2021 12:38:25 GMT"
    );
    Server =     (
        Apache
    );
    Upgrade =     (
        "h2,h2c"
    );
    Vary =     (
        "User-Agent"
    );
    "X-Powered-By" =     (
        "PHP/7.4.16"
    );
} })
response string - Optional()

非常感谢。

当您收到 500 错误时,这可能是一个格式错误的请求。或者它可能是 PHP 代码中的语法错误。或者两者兼而有之。

在这种情况下,可能是 PHP 语法错误,因为 prepare 调用末尾缺少 )。但它也可能是前者(因为 Swift 代码不是对 application/x-www-urlencoded 请求进行编码的百分比)。


当你得到500码后,查看服务器的error_log结尾(一般在你的PHP文件所在目录)。在这种情况下,我看了看它说:

[19-Jun-2021 13:45:00 America/Boise] PHP Parse error: syntax error, unexpected ';', expecting ')' in /.../insert.php on line 103

这是 PHP 中的语法错误,必须修复。


话虽如此,Swift 代码也有问题。它正在手动构建 HTTP 请求的主体。应该对请求正文中的值进行百分比编码。有图书馆,比如 Alamofire, will automatically percent encode application/x-www-urlencoded requests (which this is). If writing one’s own Swift code to build the request, you want to percent encode them manually as outlined in .

请注意,如果 (a) 您的请求包含需要百分比编码的字符(例如,spaces 或某些符号); (b) 如果您不这样做,那么这也可能导致 Web 服务器出现 500 错误。同样,error_log 文件会告诉您出了什么问题,但是如果值可能包含任何保留字符,则对 URLRequest 的正文进行百分比编码很重要。


话虽如此,还有一些其他观察结果:

  1. 问题中提供的代码不太合理。您正在将 prepare 调用的结果(这是一个“语句”)存储到名为 $sql 的变量中。 (顺便说一句,因为它是一个语句,将该变量命名为 $stmt$statement 会更有意义。)您稍后将其提供给 query。但是 query 需要一个 SQL 字符串,而不是 return 由 prepare 编辑的“语句”。相反,您应该检查 preparebind_paramexecute 中的 return 值。不要在准备、绑定、执行语句后调用 query

  2. bind_param 中的 si 值与您为五个变量提供的默认值不匹配 $_POST。您可能需要仔细检查一下。

  3. 与手头的问题没有直接关系,但我会提出一些建议。也就是说,在编写 Web 服务时,像这样,我可能会建议以下建议:

    • 设置 HTTP 状态代码(例如 http_response_code(201) 表示成功插入,http_response_code(422) 表示插入失败)。

    • 我也会 return 结果为 JSON(例如,对结果使用 PHP 关联数组,然后使用 json_encode 构建响应由 PHP) 回显,而不是文本字符串。然后客户端应用程序可以轻松解析响应。

    • 我会设置 Content-Type(例如,header("Content-Type: application/json"))以便客户端应用知道如何解析响应。

  4. 一个非常minor/subtle的观察:在PHP中,在初始<?php之前有一个space。在 PHP 开始之前不需要任何字符。它会导致非常微妙的问题。 (希望这只是在准备问题时输入的错字,但请注意。)

我在采纳了这些建议后开始工作。谢谢你们。我在 swift 中将解析更改为 JSON 并改进了 PHP 文件中变量的绑定。

PHP:

<?php

$code = $_POST["code"];

// Create connection
$con=mysqli_connect("localhost","X","X","X");

// Check connection
// Return the error code if the connection is not successful.
if (mysqli_connect_errno())
{
  echo "Failed to connect to MySQL: " . mysqli_connect_error();
}


/* create a prepared statement */
// A prepared statement is one that is loaded initially and then executed many times over and over when we attach (bind) new variables to the placeholders we have inserted. We use ? marks to signify placeholders than must be later filled with values.
$stmt = mysqli_prepare($con, "SELECT product_name, brands, Comp1Mat, Comp2Mat, Comp3Mat, Comp1Name, Comp2Name, Comp3Name FROM Products WHERE code=?");


// Bind the parameters the user has entered with the ? marks in the SQL query
mysqli_stmt_bind_param($stmt, "s", $code);

/* execute query */
mysqli_stmt_execute($stmt);

// Bind the results we get with some new variable that we will output in the print statement.
mysqli_stmt_bind_result($stmt, $product_name, $brands, $Comp1Mat, $Comp2Mat, $Comp3Mat, $Comp1Name, $Comp2Name, $Comp3Name);

/* fetch value */
mysqli_stmt_fetch($stmt);

//create an array
$emparray = array("code" => $code, "product_name" => $product_name, "brands" => $brands, "Comp1Mat" => $Comp1Mat, "Comp2Mat" => $Comp2Mat, "Comp3Mat" => $Comp3Mat, "Comp1Name" => $Comp1Name, "Comp2Name" => $Comp2Name, "Comp3Name" => $Comp3Name);
printf(json_encode($emparray));

 
// Close connections
mysqli_close($con);
?><?php

Swift:

func findSingleProduct(code: String){
        // Completes an SQL query on the Products database to find a product by it's barcode number.
        // prepare json data
        let insertProductURL = URL(string: "http://recyclingmadesimple.xyz/service.php")!
        var urlRequest = URLRequest(url: insertProductURL)
        urlRequest.httpMethod = "POST"
    
        let postString = "code=\(code)"
        urlRequest.httpBody = postString.data(using: String.Encoding.utf8)
        
        let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
            guard let data = data, error == nil else {
                print(error?.localizedDescription ?? "No data")
                return
            }
            let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
            if let responseJSON = responseJSON as? [String: Any] {
                let code = responseJSON["code"]!
                let product_name = responseJSON["product_name"]!
                let brands = responseJSON["brands"]!
                let Comp1Mat = responseJSON["Comp1Mat"]!
                let Comp2Mat = responseJSON["Comp2Mat"]!
                let Comp3Mat = responseJSON["Comp3Mat"]!
                let Comp1Name = responseJSON["Comp1Name"]!
                let Comp2Name = responseJSON["Comp2Name"]!
                let Comp3Name = responseJSON["Comp3Name"]!

                
                print(code, brands, product_name, Comp1Mat, Comp2Mat, Comp3Mat, Comp1Name, Comp2Name, Comp3Name)
                
            }
        }

        task.resume()
    }