Swift 4发送POST请求为x-www-form-urlencoded

时间:2018-02-10 12:46:57

标签: ios swift xcode swift4

我想向我的php 7服务器发送一个POST请求,该服务器接受application/x-www-form-urlencoded的数据。我拥有的数据在Struct中,我希望在提交时将此结构的每个属性作为参数。

这是处理我的urlSession请求GET和POST的结构 XHR.swift

struct XHR {

    enum Result<T> {
        case success(T)
        case failure(Error)
    }

    func urlSession<T>(method: String? = nil, file: String, data: Data? = nil, completionHandler: @escaping (Result<T>) -> Void) where T: Codable {

        let file = file.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!

        // Set up the URL request
        guard let url = URL.init(string: file) else {
            print("Error: cannot create URL")
            return
        }

        var urlRequest = URLRequest(url: url)

        if method == "POST" {
            urlRequest.httpMethod = "POST";
            urlRequest.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
            urlRequest.httpBody = data
            print(urlRequest.httpBody)
        }

        // set up the session
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)
        // vs let session = URLSession.shared

        // make the request
        let task = session.dataTask(with: urlRequest, completionHandler: {
            (data, response, error) in

            DispatchQueue.main.async { // Correct

                guard let responseData = data else {
                    print("Error: did not receive data")
                    return
                }

                let decoder = JSONDecoder()
                print(String(data: responseData, encoding: .utf8))
                do {
                    let todo = try decoder.decode(T.self, from: responseData)
                    completionHandler(.success(todo))
                } catch {
                    print("error trying to convert data to JSON")
                    //print(error)
                    completionHandler(.failure(error))
                }
            }
        })
        task.resume()
    }

}

这是向服务器发送POST请求的函数: VideoViewModel.swift

struct User: Codable {
    let username: String
    let password: String

    static func archive(w:User) -> Data {
        var fw = w
        return Data(bytes: &fw, count: MemoryLayout<User>.stride)
    }

    static func unarchive(d:Data) -> User {
        guard d.count == MemoryLayout<User>.stride else {
            fatalError("BOOM!")
        }

        var w:User?
        d.withUnsafeBytes({(bytes: UnsafePointer<User>)->Void in
            w = UnsafePointer<User>(bytes).pointee
        })
        return w!
    }
}

enum Login {
    case success(User)
    case failure(Error)
}

func login(username: String, password: String, completionHandler: @escaping (Login) -> Void) {
    let thing = User(username: username, password: password)
    let dataThing = User.archive(w: thing)

    xhr.urlSession(method: "POST", file: "https://kida.al/login_register/", data: dataThing) { (result: XHR.Result<User>) in
        switch result {
        case .failure(let error):
            completionHandler(.failure(error))
        case .success(let user):
            //let convertedThing = User.unarchive(d: user)
            completionHandler(.success(user))
        }
    }
}

我称之为:

videoViewModel.login(username: "rexhin", password: "bonbon") { (result: VideoViewModel.Login) in
    switch result {
    case .failure(let error):
        print("error")

    case .success(let user):
        print(user)
    }
}

从PHP我可以看到POST请求已成功提交但当我尝试通过username获取$_POST["username"]字段时,我得到Undefined index:

此处可以看到该应用的完整代码https://gitlab.com/rexhin/ios-kida.git

4 个答案:

答案 0 :(得分:4)

您正在传递User.archive(w: thing)的结果作为请求正文中嵌入的data,这可能永远不会有效。通常,您的archive(w:)unarchive(d:)永远不会产生任何有用的结果,您最好立即删除它们。

如果要传递需要x-www-form-urlencoded的参数,则需要创建类似URL查询的字符串。

尝试这样的事情:

func login(username: String, password: String, completionHandler: @escaping (Login) -> Void) {
    let dataThing = "username=\(username)&password=\(password)".data(using: .utf8)

    xhr.urlSession(method: "POST", file: "https://kida.al/login_register/", data: dataThing) { (result: XHR.Result<User>) in
        //...
    }
}

上面的示例有点简化,您可能需要在将username和/或password嵌入字符串之前将其转义,此时它们可以包含一些特殊字符。你可以在网上找到很多关于它的文章。

答案 1 :(得分:1)

引用此post

  

在PHP中,从未设置的变量或数组元素是   不同于其值为null的人;试图访问这样的   未设置的值是运行时错误。

当您尝试访问未设置的变量或数组元素时,会发生未定义的索引错误。您应该使用函数isset以安全地从POST正文访问用户名参数。在PHP文件中尝试以下代码。

if (isset($_POST["username"]))
{
  $user= $_POST["username"];
  echo 'Your Username is ' . $user;
} 
else 
{
  $user = null;
  echo "No user name found";
}

答案 2 :(得分:0)

我在Swift 4中使用了以下代码

  guard let url = URL(string: "http://192.168.88.129:81/authenticate") else {
        return
    }


    let user1 = username.text!
    let pass = passwordfield.text!
    print(user1)
    print(pass)
    let data : Data = "username=\(user1)&password=\(pass)&grant_type=password".data(using: .utf8)!
    var request : URLRequest = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField:"Content-Type");
    request.setValue(NSLocalizedString("lang", comment: ""), forHTTPHeaderField:"Accept-Language");
    request.httpBody = data

    print("one called")

    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)
    // vs let session = URLSession.shared
      // make the request
    let task = session.dataTask(with: request, completionHandler: {
        (data, response, error) in

         if let error = error
        {
            print(error)
        }
         else if let response = response {
            print("her in resposne")

        }else if let data = data
         {
            print("here in data")
            print(data)
        }

        DispatchQueue.main.async { // Correct

            guard let responseData = data else {
                print("Error: did not receive data")
                return
            }

            let decoder = JSONDecoder()
            print(String(data: responseData, encoding: .utf8))
            do {
              //  let todo = try decoder.decode(T.self, from: responseData)
              //  NSAssertionHandler(.success(todo))
            } catch {
                print("error trying to convert data to JSON")
                //print(error)
              //  NSAssertionHandler(.failure(error))
            }
        }
    })
    task.resume()


}

答案 3 :(得分:0)

另一种方法如下:

  1. URLEncodedFormEncoder.swift 添加到您的项目中。这是来自 Alamofire / Vapor 的自定义 URLEncodedFormEncoder。

  2. 使您的模型符合原生 Swift Encodable 协议,就像您使用 JSON 编码一样。

  3. 像在 json 编码期间一样对模型进行编码

// example
let requstModel = OpenIDCTokenRequest(
                             clientId: clientId,
                             clientSecret: clientSecret,
                             username: username,
                             password: password
                  )

guard let requestData: Data = try? URLEncodedFormEncoder().encode(requstModel) else {
    return // handle encoding error
}