Solicitud HTTP en Swift con método POST

Resuelto angeant asked hace 9 años • 7 respuestas

Estoy intentando ejecutar una solicitud HTTP en Swift para PUBLICAR 2 parámetros en una URL.

Ejemplo:

Enlace:www.thisismylink.com/postName.php

Parámetros:

id = 13
name = Jack

¿Cuál es la forma más sencilla de hacerlo?

Ni siquiera quiero leer la respuesta. Solo quiero enviar eso para realizar cambios en mi base de datos a través de un archivo PHP.

angeant avatar Oct 14 '14 22:10 angeant
Aceptado

La clave es que quieres:

  • establezca el httpMethoden POST;
  • opcionalmente, establezca el Content-Typeencabezado para especificar cómo se codificó el cuerpo de la solicitud, en caso de que el servidor acepte diferentes tipos de solicitudes;
  • opcionalmente, establezca el Acceptencabezado para solicitar cómo se debe codificar el cuerpo de la respuesta, en caso de que el servidor pueda generar diferentes tipos de respuestas; y
  • configure el httpBodypara que se codifique correctamente para el específico Content-Type; por ejemplo, si se application/x-www-form-urlencodedsolicita, necesitamos codificar porcentualmente el cuerpo de la solicitud.

Por ejemplo, en Swift 3 y versiones posteriores puedes:

let url = URL(string: "https://httpbin.org/post")!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.httpMethod = "POST"
let parameters: [String: Any] = [
    "id": 13,
    "name": "Jack & Jill"
]
request.httpBody = parameters.percentEncoded()

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    guard 
        let data = data, 
        let response = response as? HTTPURLResponse, 
        error == nil 
    else {                                                               // check for fundamental networking error
        print("error", error ?? URLError(.badServerResponse))
        return
    }
    
    guard (200 ... 299) ~= response.statusCode else {                    // check for http errors
        print("statusCode should be 2xx, but is \(response.statusCode)")
        print("response = \(response)")
        return
    }
    
    // do whatever you want with the `data`, e.g.:
    
    do {
        let responseObject = try JSONDecoder().decode(ResponseObject<Foo>.self, from: data)
        print(responseObject)
    } catch {
        print(error) // parsing error
        
        if let responseString = String(data: data, encoding: .utf8) {
            print("responseString = \(responseString)")
        } else {
            print("unable to parse response as string")
        }
    }
}

task.resume()

Donde las siguientes extensiones facilitan el cuerpo de la solicitud con codificación porcentual, la conversión de un Swift Dictionarya uno application/x-www-form-urlencodedformateado Data:

extension Dictionary {
    func percentEncoded() -> Data? {
        map { key, value in
            let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
            let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
            return escapedKey + "=" + escapedValue
        }
        .joined(separator: "&")
        .data(using: .utf8)
    }
}

extension CharacterSet { 
    static let urlQueryValueAllowed: CharacterSet = {
        let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
        let subDelimitersToEncode = "!$&'()*+,;="
        
        var allowed: CharacterSet = .urlQueryAllowed
        allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
        return allowed
    }()
}

Y los siguientes Decodableobjetos modelo facilitan el análisis de la application/jsonrespuesta usando JSONDecoder:

// sample Decodable objects for https://httpbin.org

struct ResponseObject<T: Decodable>: Decodable {
    let form: T    // often the top level key is `data`, but in the case of https://httpbin.org, it echos the submission under the key `form`
}

struct Foo: Decodable {
    let id: String
    let name: String
}

Esto busca tanto errores de red fundamentales como errores HTTP de alto nivel. Este porcentaje también escapa correctamente a los parámetros de la consulta.

Tenga en cuenta que utilicé of name, para ilustrar el resultado Jack & Jilladecuado de , que está "codificado por porcentaje" (es decir, el espacio se reemplaza con y el valor se reemplaza con ).x-www-form-urlencodedname=Jack%20%26%20Jill%20&%26


Consulte la revisión anterior de esta respuesta para la interpretación de Swift 2.

Rob avatar Oct 14 '2014 15:10 Rob

Swift 4 y superior

func postRequest() {
  
  // declare the parameter as a dictionary that contains string as key and value combination. considering inputs are valid
  
  let parameters: [String: Any] = ["id": 13, "name": "jack"]
  
  // create the url with URL
  let url = URL(string: "www.thisismylink.com/postName.php")! // change server url accordingly
  
  // create the session object
  let session = URLSession.shared
  
  // now create the URLRequest object using the url object
  var request = URLRequest(url: url)
  request.httpMethod = "POST" //set http method as POST
  
  // add headers for the request
  request.addValue("application/json", forHTTPHeaderField: "Content-Type") // change as per server requirements
  request.addValue("application/json", forHTTPHeaderField: "Accept")
  
  do {
    // convert parameters to Data and assign dictionary to httpBody of request
    request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
  } catch let error {
    print(error.localizedDescription)
    return
  }
  
  // create dataTask using the session object to send data to the server
  let task = session.dataTask(with: request) { data, response, error in
    
    if let error = error {
      print("Post Request Error: \(error.localizedDescription)")
      return
    }
    
    // ensure there is valid response code returned from this HTTP response
    guard let httpResponse = response as? HTTPURLResponse,
          (200...299).contains(httpResponse.statusCode)
    else {
      print("Invalid Response received from the server")
      return
    }
    
    // ensure there is data returned
    guard let responseData = data else {
      print("nil Data received from the server")
      return
    }
    
    do {
      // create json object from data or use JSONDecoder to convert to Model stuct
      if let jsonResponse = try JSONSerialization.jsonObject(with: responseData, options: .mutableContainers) as? [String: Any] {
        print(jsonResponse)
        // handle json response
      } else {
        print("data maybe corrupted or in wrong format")
        throw URLError(.badServerResponse)
      }
    } catch let error {
      print(error.localizedDescription)
    }
  }
  // perform the task
  task.resume()
}
Suhit Patil avatar Dec 11 '2016 04:12 Suhit Patil

Para cualquiera que busque una forma limpia de codificar una solicitud POST en Swift 5.

No necesita lidiar con la adición manual de codificación porcentual. Úselo URLComponentspara crear una URL de solicitud GET. Luego use queryla propiedad de esa URL para obtener el porcentaje de cadena de consulta con escape adecuado.

let url = URL(string: "https://example.com")!
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)!

components.queryItems = [
    URLQueryItem(name: "key1", value: "NeedToEscape=And&"),
    URLQueryItem(name: "key2", value: "vålüé")
]

let query = components.url!.query

Será queryuna cadena con escape adecuado:

key1=NeedToEscape%3DAnd%26&key2=v%C3%A5l%C3%BC%C3%A9

Ahora puedes crear una solicitud y usar la consulta como HTTPBody:

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = Data(query.utf8)

Ahora puedes enviar la solicitud.

pointum avatar Oct 12 '2019 17:10 pointum