当从iOS App(Swift)请求时,PHP不响应

时间:2019-02-14 05:49:37

标签: php ios swift multipartform-data

我在用Swift 4编写的iOS应用中编写了一个简单的HTTP POST请求。

Swift代码如下:

let url = URL(string: "http://example.com/api.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"

let boundary = "Boundary-\(NSUUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

let body = NSMutableData()

// Text parameter: Action
body.append(NSString(format: "\r\n--%@\r\n", boundary).data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format: "Content-Disposition: form-data; name=\"action\"\r\n\r\n" as NSString).data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format: ("my_action" as NSString)).data(using: String.Encoding.utf8.rawValue)!)

// Text parameter: Peer ID
body.append(NSString(format: "\r\n--%@\r\n", boundary).data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format: "Content-Disposition: form-data; name=\"peerid\"\r\n\r\n" as NSString).data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format: ("123456" as NSString)).data(using: String.Encoding.utf8.rawValue)!)

// Image
body.append(NSString(format: "\r\n--%@\r\n", boundary).data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format:"Content-Disposition: form-data; name=\"secret_img\"; filename=\"secret.jpg\"\r\n").data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format: "Content-Type: application/octet-stream\r\n\r\n").data(using: String.Encoding.utf8.rawValue)!)
body.append(imageData!)
body.append(NSString(format: "\r\n--%@\r\n", boundary).data(using: String.Encoding.utf8.rawValue)!)

request.httpBody = body as Data

let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
    guard error == nil else {
        return
    }

    guard let data = data else {
        return
    }

    do {
        // create json object from response Json data 
        /*if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
         print(json)
         // handle json...
        }*/

        // Debug: check the response string
        let responseString = String(data: data, encoding: .utf8)
        print("responseString = \(responseString)")
    } catch let error {
        print(error.localizedDescription)
    }
})
task.resume()

PHP响应为空字符串(未报告错误)。然后,我使用一个简单的HTML表单来测试PHP脚本是否还可以。这是form.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test Upload Form</title>
</head>

<body>
<form action="index.php" method="post" enctype="multipart/form-data">
    <input type="hidden" name="action" value="my_action" />
    <input type="hidden" name="peerid" value="123456" />
    <p>Image: <input type="file" name="secret_img" required /></p>
    <p><input type="submit" name="submit" id="submit" /></p>
</form>
</body>
</html>

PHP响应:

{"status": "OK"}

证明脚本没有问题。 PHP脚本如下,非常简单:

<?php
header('Content-Type: application/json');
if(!isset($_FILES['secret_img'])) {
    echo json_encode(array('error' => 'missing image'));
    exit;
}
$file = $_FILES['secret_img'];
$upload_folder = 'upload/';
if(move_uploaded_file($_FILES['secret_img']['tmp_name'], $upload_folder . $_POST['peerid'] . '_' . time() . '_' . rand(1000, 999999) . '.jpg')) {
    // success
    echo json_encode(array('status' => 'OK'));
} else {
    echo json_encode(array('error' => 'unable to copy image'));
}
?>

我将错误缩小为Swift代码。我删除了body中的内容(注释了body.append()行),然后PHP响应如下:

{"error": "missing image"}

这是正常行为。

你们能帮我发现问题吗?谢谢。


更新,如果我在request.httpBody行之前添加以下两行,则dump()返回nil。发生了什么事?

let debug = NSString(data: body as Data, encoding: String.Encoding.utf8.rawValue)
dump(debug)

body变量已正确初始化,并且没有错误地附加了数据。

1 个答案:

答案 0 :(得分:1)

问题很细微,不易发现。问题的原因是:

  • 边界设置不正确
  • 一些变更行\r\n丢失/重复

要解决此问题,httpBody必须更改为以下内容:

let body = NSMutableData()

// Text parameter: Action
body.append(NSString(format: "--%@\r\n", boundary).data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format: "Content-Disposition: form-data; name=\"action\"\r\n\r\n" as NSString).data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format: "my_action\r\n").data(using: String.Encoding.utf8.rawValue)!)

在第一个块中,不需要第一个\r\n(这引起httpBody中的空行),并且(与格式对齐),第一个块中的\r\n第二个块将移到my_action字符串的结尾。

// Text parameter: Peer ID
body.append(NSString(format: "--%@\r\n", boundary).data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format: "Content-Disposition: form-data; name=\"peerid\"\r\n\r\n").data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format: "123456\r\n").data(using: String.Encoding.utf8.rawValue)!)

与第一个块相同,\r\n被重新排列(与403 Forbidden问题无关)。还简化了一些代码。

// Image
body.append(NSString(format: "--%@\r\n", boundary).data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format:"Content-Disposition: form-data; name=\"secret_img\"; filename=\"secret.jpg\"\r\n").data(using: String.Encoding.utf8.rawValue)!)
body.append(NSString(format: "Content-Type: application/octet-stream\r\n\r\n").data(using: String.Encoding.utf8.rawValue)!)
body.append(imageData!)
body.append(NSString(format: "\r\n--%@--\r\n", boundary).data(using: String.Encoding.utf8.rawValue)!)

request.httpBody = body as Data

最后一个块有问题,导致403 Forbidden错误。最后一行错过了结束的边界符号。

结果如下:

--boundary
Content-Disposition: form-data; name="action"
my_action

--boundary
Content-Disposition: form-data; name="peerid"
123456

--boundary
Content-Disposition: form-data; name="secret_img"; filename="secret.jpg"
{image data here}

--boundary--

结论:缺少的结束边界导致403禁止(格式错误的HTTP正文)。

此修复程序的灵感来自MDN的Content-Disposition文档: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition