Reputation: 23
Header:
let header = ["Content-Type" : "application/x-www-form-urlencoded", "Authorization" : "Basic " + self.basicAuth];
Body:
var body : [String : AnyObject] = [:];
let body = ["grant_type" : "client_credentials", "scope" : "MessageSender"];
The Request and Serialization:
private func makeHTTPPostRequest(path: String, header: [String : String], body: [String: AnyObject], onCompletion: @escaping ServiceResponse) {
let request = NSMutableURLRequest(url: NSURL(string: path)! as URL)
// Set the method to POST
request.httpMethod = "POST"
do {
// Set the POST body for the request
let jsonBody = try JSONSerialization.data(withJSONObject: body, options: .prettyPrinted)
request.httpBody = jsonBody
let session = URLSession.shared
request.allHTTPHeaderFields = header;
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
if let httpResponse = response as? HTTPURLResponse {
if let jsonData = data {
let json:JSON = JSON(data: jsonData)
print(response)
print(json)
onCompletion(json,httpResponse, error as NSError?)
} else {
onCompletion(JSON.null,HTTPURLResponse.init(), error as NSError?)
}
}
})
task.resume()
} catch {
onCompletion(JSON.null,HTTPURLResponse.init(), nil)
}
}
}
When the request is done, it fires a 400 response with
{
"error_description" : "grant_type parameter is requiered field and it has to be non empty string.",
"error" : "invalid_request"
}
Obviously the body is not set correctly but I really don´t know why. I´m using this piece of code in other applications with no problem... . The same request works like charm in Postman. The body in postman is set with type x-www-form-urlencoded. Maybe the JSONSerialization is wrong ?
Upvotes: 1
Views: 670
Reputation: 47896
To send a POST request with Content-Type: application/x-www-form-urlencoded;
, you need to create a URL query-like String and then convert it to a Data. Your code or any Swift Standard Library functions do not have the functionality. You may need to write it by yourself, or find a suitable third-party library. (Of course JSONSerialization
is not suitable here, the String is not a JSON.)
With given a Dictionary<String, String>
, you can do it like this:
var body: [String: String] = [:]
body = ["grant_type": "client_credentials", "scope": "MessageSender"]
(Simplified...)
request.httpBody = body.map{"\($0)=\($1)"}.joined(separator: "&").data(using: .utf8)
//`body.map{"\($0)=\($1)"}.joined(separator: "&")` -> grant_type=client_credentials&scope=MessageSender
(Strict... 4.10.22.6 URL-encoded form data)
extension CharacterSet {
static let wwwFormUrlencodedAllowed = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._*" + "+")
}
extension String {
var wwwFormUrlencoded: String {
return self
.replacingOccurrences(of: " ", with: "+")
.addingPercentEncoding(withAllowedCharacters: .wwwFormUrlencodedAllowed)!
}
}
class HTTPBody {
static func wwwFormUrlencodedData(withDictionary dict: [String: String]) -> Data {
return body
.map{"\($0.wwwFormUrlencoded)=\($1.wwwFormUrlencoded)"}
.joined(separator: "&").data(using: .utf8)!
}
}
request.httpBody = HTTPBody.wwwFormUrlencodedData(withDictionary: body)
(Remember, not many servers interpret the received form data as strictly generated.)
One more, this is not a critical issue in this case, but you should better use Swift classes rather than NS-
something:
typealias ServiceResponse = (JSON, HTTPURLResponse?, Error?)->Void
private func makeHTTPPostRequest(path: String, header: [String : String], body: [String: String], onCompletion: @escaping ServiceResponse) {
var request = URLRequest(url: URL(string: path)!)
// Set the method to POST
request.httpMethod = "POST"
// Set the POST body for the request (assuming your server likes strict form data)
request.httpBody = HTTPBody.wwwFormUrlencodedData(withDictionary: body)
let session = URLSession.shared
request.allHTTPHeaderFields = header;
let task = session.dataTask(with: request, completionHandler: {data, response, error -> Void in
if let httpResponse = response as? HTTPURLResponse {
if let jsonData = data {
let json:JSON = JSON(data: jsonData)
print(response)
print(json)
onCompletion(json, httpResponse, error)
} else {
onCompletion(JSON.null, httpResponse, error)
}
}
})
task.resume()
}
Upvotes: 1