【问题标题】:Swift 4 send POST request as x-www-form-urlencodedSwift 4 以 x-www-form-urlencoded 形式发送 POST 请求
【发布时间】:2018-07-21 02:45:25
【问题描述】:

我想向我的 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 请求已成功提交,但是当我尝试通过执行 $_POST["username"] 获取 username 字段时,我得到 Undefined index:

应用程序的完整代码可以在这里看到https://gitlab.com/rexhin/ios-kida.git

【问题讨论】:

    标签: ios swift xcode swift4


    【解决方案1】:

    我在 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()
    
    
    }
    

    【讨论】:

    • 自去年以来我一直遇到表单数据问题,我使用的是 alamofire temporary 。但是,最后发现这段代码很有用,它使用纯编码传递参数。非常感谢!
    【解决方案2】:

    您将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 嵌入到字符串之前转义它们,因为它们可以包含一些特殊字符。你可以在网上找到很多关于它的文章。

    【讨论】:

    • 是的,但我每次发送 POST 请求时都需要手动执行此操作。这是一种将结构转换为"username=\(username)&amp;password=\(password)" 的方法吗?
    • @RexhinHoxha,是的,手动操作。如果你想问一些关于任何结构的问题,那是另一个问题。在这个问题中,你写了 this struct.
    • 如果用户名或密码的值需要进行 URL 编码,这可能不起作用。
    • @JeremyWhite,感谢您的澄清。 上面的例子是...部分也提到了同样的事情,但你的评论会更好。
    【解决方案3】:

    引用此post

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

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

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

    【讨论】:

    • 我知道但变量不是 SET
    • 您是否从应用中正确传递数据?
    • 这就是问题所在,我认为没有
    • 您是否在将字符串数据发送到服务器之前对其进行编码?示例:[yourString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
    【解决方案4】:

    另一种方法如下:

    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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-19
      • 1970-01-01
      • 2017-12-18
      • 1970-01-01
      • 2014-03-30
      • 1970-01-01
      • 2018-10-10
      • 2018-03-24
      相关资源
      最近更新 更多