I\'ve a problem when I try to send a POST request to my API on my server, I\'ve followed many many different tutorials but it still doesn\'t work. I know than my problem is
the following php code is for receiving application/url+encode
encoded post message. please refer https://en.wikipedia.org/wiki/Percent-encoding
$_POST["pseudo"]
and your swift code was sending a JSON
encoded string data. They are incompatible.
If you don't want to change the php code, in Swift you should send url-encode format message, li:
// UPDATED with corrections from @Rob
var params = ["param1":"value1", "papam2": "value 2"]
var body = ""
for (key, value) in params {
body = body.stringByAppendingString(key)
body = body.stringByAppendingString("=")
body = body.stringByAppendingString(value.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!)
body = body.stringByAppendingString("&")
}
body = body.substringToIndex(advance(body.startIndex, countElements(body)-1)) // remove the last "&"
request.HTTPBody = body.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
As others have pointed out, the encoding of the request is not quite right. Your server code is not expecting JSON request, but rather is using $_POST
variables (which means that the request should have Content-Type
of application/x-www-form-urlencoded
). So that's what you should create. For example in Swift 3 and later:
var request = URLRequest(url: url)
request.httpMethod = "POST"
let parameters = ["somekey" : "valueforkey"]
request.setContent(with: parameters)
let task = session.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error ?? "Unknown error")
return
}
// your response parsing code here
}
task.resume()
The setContent
method would take a dictionary of the form ["key1": "foo", "key2" : "bar"]
and populate the httpBody
with something that looks like key1=foo&key2=bar
. If you do that, the server will be able to parse the $_POST
from the request.
extension URLRequest {
/// Set body and header for x-www-form-urlencoded request
///
/// - Parameter parameters: Simple string dictionary of parameters to be encoded in body of request
mutating func setContent(with parameters: [String: String]) {
setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let array = parameters.map { entry -> String in
let key = entry.key.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)!
let value = entry.value.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)!
return key + "=" + value
}
httpBody = array.joined(separator: "&").data(using: .utf8)!
}
}
Note, this also percent-encodes the values (and, just in case, the keys, too), which is critical. While others have advised using addingPercentEncoding
with .urlQueryAllowed
, sadly that will not do the job, as it will let certain reserved characters (notably +
) pass unescaped. Most web services will interpret the +
as a space, in conformance with the x-www-form-urlencoded spec. So, adapting an approach adopted by Alamofire, I define a character set which will encode values with the various delimiters:
extension CharacterSet {
/// Returns the character set for characters allowed in the individual parameters within a query URL component.
///
/// The query component of a URL is the component immediately following a question mark (?).
/// For example, in the URL `http://www.example.com/index.php?key1=value1#jumpLink`, the query
/// component is `key1=value1`. The individual parameters of that query would be the key `key1`
/// and its associated value `value1`.
///
/// According to RFC 3986, the set of unreserved characters includes
///
/// `ALPHA / DIGIT / "-" / "." / "_" / "~"`
///
/// In section 3.4 of the RFC, it further recommends adding `/` and `?` to the list of unescaped characters
/// for the sake of compatibility with some erroneous implementations, so this routine also allows those
/// to pass unescaped.
static var 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
}()
}
For Swift 2 rendition, see previous revision of this answer.
Try this:
let myURL = NSURL(string: "http://localhost:8888/academy/test.php")!
let request = NSMutableURLRequest(URL: myURL)
request.HTTPMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
let bodyStr:String = "pseudo=test"
request.HTTPBody = bodyStr.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
// Your completion handler code here
}
task.resume()
You have to encode your data using UTF8 string encoding. If you need to set more than one field&value pairs for request body, you can change the body string, for example, "pseudo=test&language=swift". In fact, I usually create an extension for NSMutableURLRequest and add a method which takes a dictionary as parameter and sets the content of this map(dictionary) as HTTPBody using correct encoding. This may work for you:
extension NSMutableURLRequest {
func setBodyContent(contentMap: Dictionary<String, String>) {
var firstOneAdded = false
let contentKeys:Array<String> = Array(contentMap.keys)
for contentKey in contentKeys {
if(!firstOneAdded) {
contentBodyAsString += contentKey + "=" + contentMap[contentKey]!
firstOneAdded = true
}
else {
contentBodyAsString += "&" + contentKey + "=" + contentMap[contentKey]!
}
}
contentBodyAsString = contentBodyAsString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
self.HTTPBody = contentBodyAsString.dataUsingEncoding(NSUTF8StringEncoding)
}
}
And you can use this as:
request.setBodyContent(params)
I hope this helps you!