PHP ничего не отвечает при запросе из iOS App (Swift) - PullRequest
0 голосов
/ 14 февраля 2019

Я написал простой HTTP-запрос POST в своем iOS-приложении, написанном на Swift 4.

Код 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"}

, что является нормальным поведением.

Можете ли вы, ребята, помочь мнеопределить проблему?Спасибо.


ОБНОВЛЕНИЕ Если я добавлю следующие 2 строки перед строкой request.httpBody, dump() вернет nil.Что происходит?

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

переменная body правильно инициализирована и данные добавляются без ошибок.

1 Ответ

0 голосов
/ 14 февраля 2019

Проблема неуловима и ее нелегко обнаружить.Причина проблемы:

  • граница установлена ​​неправильно
  • некоторые строки изменения \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 Запрещенной проблеме).Также упрощены некоторые коды.

// 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).

Исправление основано на документации Content-Disposition в MDN: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition

...