Solicitud HTTP en Swift con método POST
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.
La clave es que quieres:
- establezca el
httpMethod
enPOST
; - opcionalmente, establezca el
Content-Type
encabezado 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
Accept
encabezado 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
httpBody
para que se codifique correctamente para el específicoContent-Type
; por ejemplo, si seapplication/x-www-form-urlencoded
solicita, 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 Dictionary
a uno application/x-www-form-urlencoded
formateado 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 Decodable
objetos modelo facilitan el análisis de la application/json
respuesta 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 & Jill
adecuado de , que está "codificado por porcentaje" (es decir, el espacio se reemplaza con y el valor se reemplaza con ).x-www-form-urlencoded
name=Jack%20%26%20Jill
%20
&
%26
Consulte la revisión anterior de esta respuesta para la interpretación de Swift 2.
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()
}
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 URLComponents
para crear una URL de solicitud GET. Luego use query
la 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á query
una 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.