(iOS / Swift) Простой почтовый запрос с ключом-значением не выполнен, но успешно с почтальоном - PullRequest
0 голосов
/ 28 октября 2019

Я создал простой POST-запрос, используя Postman, и это удалось. Это скриншот тела и ответа

тело и ответа

Это скриншот заголовка в заголовке Почтальона

Теперь я создаю POST-запрос в приложении для iOS, используя Swift. Это код:

public func getResultItem(url: URL, _ completion:@escaping (ResultItem?, Error?) -> Void){
    let parameter = ["uname":"21120113120038", "pass":"123456"]

    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField:"Content-Type")
    guard let httpBody = try? JSONSerialization.data(withJSONObject: parameter, options: []) else {return}
    request.httpBody = httpBody

    URLSession.shared.dataTask(with: request) { (data, response, error) in
        var resultItem: ResultItem?

        guard let data = data else {
            completion(resultItem, error)
            return
        }

        do {
            resultItem = try JSONDecoder().decode(ResultItem.self, from: data)
            completion(resultItem, error)
        } catch let error as NSError{
            completion(resultItem, error)
        }
    }.resume()
}

Проблема в том, что «данные» из обработчика завершения dataTask всегда равны 0 байт. Похоже, мой запрос недействителен, поэтому он не возвращает JSON. Поскольку данные имеют размер 0 байт, блок «do» всегда завершается ошибкой при декодировании JSON и превращается в блок «catch». Error.description в этом блоке «catch» здесь -> «Указанные данные не были действительными в формате JSON».

Какая часть моего кода неверна? Мое первое предположение - проблема с запросом. Может быть, параметр или запрос httpbody неверен, но я не знаю, как это исправить.

Спасибо

1 Ответ

0 голосов
/ 28 октября 2019

Ваш тип контента - application/x-www-form-urlencoded, но вы задаете тело в виде JSON вместо параметров в кодировке URL. То, как ваше тело HTTP выглядит так, как у вас сейчас, выглядит примерно так:

{"uname":"21120113120038","pass":"123456"}

... но вы хотите, чтобы оно выглядело так:

uname=21120113120038&pass=123456

Итак, заменитеэта строка:

guard let httpBody = try? JSONSerialization.data(withJSONObject: parameter, options: []) else {return}

... с чем-то вроде этого:

guard let httpBody = parameter.map({
    [$0.addingPercentEncoding(withAllowedCharacters: .alphanumerics) ?? "",
     $1.addingPercentEncoding(withAllowedCharacters: .alphanumerics) ?? ""].joined(separator: "=")
}).joined(separator: "&").data(using: .utf8) else { return }

(Тем не менее, я думаю, что это немного грязно, поэтому вот некоторые расширения, чтобы немного его очистить:)

extension String {
    var urlEncodedQueryKeyOrValue: String {
        return addingPercentEncoding(withAllowedCharacters: .alphanumerics) ?? ""
    }
}

extension Dictionary where Key == String, Value == String {
    var wwwFormURLEncodedString: String {
        return map { "\($0.urlEncodedQueryKeyOrValue)=\($1.urlEncodedQueryKeyOrValue)" }.joined(separator: "&")
    }
}

И тогда вы будете использовать это так:

[...]
guard let httpBody = parameter.wwwFormURLEncodedString.data(using: .utf8) else { return }
request.httpBody = httpBody
[...]
...