Что не так с моим MultiPartFormData Encoder? Swift 4 - PullRequest
0 голосов
/ 16 февраля 2020

Hello) Это мой MultiPartFormDataEncoder:

            let boundary =  "Boundary-\(UUID().uuidString)"

            var body = ""

            if let parameters = parameters {
                for (key, value) in parameters {
                    body += "--\(boundary)\r\n"
                    body += "Content-Disposition:form-data; name=\"\(String(describing: key))\"\r\n\r\n"
                    body += "\(String(describing: value))\r\n"
                }
            }

            if let files = files {
                for file in files {
                    body += "--\(boundary)\r\n"
                    body += "Content-Disposition: form-data; name=\"\(file.key)\"; filename=\"\(file.name)\"\r\n"
                    body += "Content-Type: \(file.type)\r\n\r\n"
                    body += "\(file.data)\r\n"
                }
            }

            body += "--".appending(boundary.appending("--"))

            urlRequest.httpBody = body.data(using: .utf8)

            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                urlRequest.setValue("multipart/form-data; charset=utf8; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
            }
            urlRequest.timeoutInterval = 60
            urlRequest.httpShouldHandleCookies = false

После отправки запроса на сервер я получил "Bad Request". Когда я пытаюсь сделать это у почтальона, запрос оказывается успешным. Все параметры и файлы имеют правильные имена, типы и ключи. Я проверил наверняка. Так что проблема не в BackEnd, потому что он получает мой запрос и видит правильное тело, но он ломается после получения.

Я думаю, что-то не так с заголовками.

Итак, что вы думаете?

Редактировать: После правильного разбора ответа я вижу следующее сообщение:

{"image": ["Загрузить действительное изображение. Файл Вы загрузили не изображение или не поврежденное изображение. "]}

1 Ответ

0 голосов
/ 18 февраля 2020

Итак, я нашел проблему и решение.

Ошибка была в этой строке:

body += "\(file.data)\r\n"

Как говорится @ Larme , мы не можем поместить данные и строка в одну строку.

Лучше Решение будет добавлять данные и строку отдельно, например:

// old body += "\(file.data)\r\n"

// new:
var body: Data = Data()

body.append(file.data)
body.append("\r\n".data(using: .utf8)!)

В моем случае MultiPartFormDataParameterEncoder будет выглядеть так:

func encode(urlRequest: inout URLRequest, with parameters: Parameters?, files: Files?) throws {
        do {
            let boundary =  "Boundary-\(UUID().uuidString)"

            var body: Data = Data()

            if let parameters = parameters {
                for (key, value) in parameters {
                    try body.appendString("--\(boundary)\r\n")
                    try body.appendString("Content-Disposition:form-data; name=\"\(String(describing: key))\"\r\n\r\n")
                    try body.appendString("\(String(describing: value))\r\n")
                }
            }

            if let files = files {
                for file in files {
                    try body.appendString("--\(boundary)\r\n")
                    try body.appendString("Content-Disposition: form-data; name=\"\(file.key)\"; filename=\"\(file.name)\"\r\n")
                    try body.appendString("Content-Type: \"\(file.type)\"\r\n\r\n")
                    body.append(file.data)
                    try body.appendString("\r\n")
                }
            }

            try body.appendString("--".appending(boundary.appending("--")))

            urlRequest.httpBody = body

            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
            }
        } catch {
            throw NetworkError.encodingMultiPartFormDataFailed
        }
    }

public enum NetworkError: String, Error {
    case encodingMultiPartFormDataFailed = "Parameter encoding in MultiPartFormData failed ..."
}

Также необходимо добавить файлы и параметры моделей:

public typealias Parameters = [String:Any]
public typealias Files = [FileModel]

public struct FileModel {
    let name: String
    let key: String
    let data: Data
    let type: String

    init(name: String, type: FileType, data: Data) {
        self.name = name
        self.key = type.key // will be image or audio
        self.data = data
        self.type = type.rawValue // will be "audio/mp3" or "image/jpeg"
    }
}

extension FileModel {
    enum FileType: String {
        case image = "image/jpeg"
        case audio = "audio/mp3"

        var key: String {
            return String(describing: self)
        }
    }
}

// USAGE EXAMPLE
FileModel(name: name, type: .image, data: imageJpegData)

Также не забыли расширение данных для большей безопасности:

extension Data {
    private enum DataError: String, Error {
        case encodingStringToDataFailed = "Encoding String to Data failed ..."
    }

    mutating func appendString(_ string: String) throws {
        guard let data: Data = string.data(using: String.Encoding.utf8, allowLossyConversion: false) else {
            throw DataError.encodingStringToDataFailed
        }
        append(data)
    }
}

Спасибо ^ _ ^

...