So I'm trying to do an async POST request to my server from my iOS device.
Here's the Swift code I've written:
var urlString = "https://eamorr.com/ajax/login.php"
var url = NSURL(string: urlString)
var request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
var params = ["uname":"Eamorr", "pword":"mySecret"] as Dictionary<String, String>
request.HTTPBody = params. //what to do here???
var connection = NSURLConnection(request: request, delegate: self, startImmediately: true)
The problem is, how do (reliably) convert the params to a POST string? (My server is expecting plain old POST parameters... Nothing fancy - no JSON, etc.)
I need this to be very reliable (i.e. not a hack), so I'd prefer if there was some base class (written by Apple) that I could use.
Does anyone have any ideas?
Some of what bbarnhart suggests, but to actually create the body data, use:
let body = "&".join(map(params, {
let key = $0.0.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let value = $0.1.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
return "\(key!)=\(value!)"
}))
request.HTTPBody = body.dataUsingEncoding(NSUTF8StringEncoding)
Basically escape and convert each item into "key=value" and then string them all together separated by "&"
You'll also (probably) need to set the Content-Type header as recommended by bbarnhart:
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
To configure your post you need to do three things.
Set Content-Type to application/x-www-form-urlencoded
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
Set the body as a key/value delimited by ampersand string and convert to NSData:
let myPostBody = "uname=Eamorr&pword=mySecret"
request.HTTPBoddy = (myPostBody as NSString).dataUsingEncoding(NSUTF8StringEncoding)
Set the Content-Length:
request.addValue(String(myPostBody.length), forHTTPHeaderField: "Content-Length")
Related
I'm trying to send a Integer value to WebService with body so I created a function like that :
static func GetRoomVersionAndRemainingSeconds(auctionId:Int,completed:#escaping (ServiceResultDene<String>)->()){
var memberJson : String = ""
do{
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(auctionId)
memberJson = String(data: jsonData, encoding: String.Encoding.utf8)!
}catch{
print(error)
}
var request = URLRequest(url: URL(string: WebServiceUrls.GetRoomVersionAndRemainingSeconds)!)
request.httpMethod = HTTPMethod.post.rawValue
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(Util.cookie, forHTTPHeaderField: "Cookie")
request.httpBody = (memberJson).data(using: .unicode)
Alamofire.request(request).responseJSON{response in
....
}
When I try above code , it perfectly encode the Integer value on IOS 13+ -- memberJson is = "\"4\""
But when I try this on IOS 12.4 It doesnt encode and error.localizedDescription returns : invalidValue(4, Swift.EncodingError.Context(codingPath: [], debugDescription: "Top-level Int encoded as number JSON fragment.", underlyingError: nil))
What is the problem is here ? Any suggestion could be good.Thanks
The JSON encoding is pointless. In practice you are going to convert an Int to a String.
This can be done much simpler
static func GetRoomVersionAndRemainingSeconds(auctionId: Int,completed: #escaping (ServiceResultDene<String>)->()){
var request = URLRequest(url: URL(string: WebServiceUrls.GetRoomVersionAndRemainingSeconds)!)
request.httpMethod = HTTPMethod.post.rawValue
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(Util.cookie, forHTTPHeaderField: "Cookie")
request.httpBody = Data(String(auctionId).utf8)
Alamofire.request(request).responseJSON{response in
....
}
By the way the reason of the error is that JSONEncoder doesn’t support to encode fragments prior to iOS 13.
Here is my function for building a request to send to a server. This code works perfectly fine when it's a small, plain-text file. However, when I pass it a binary file, it crashes on the line indicated below (when it tries to convert that to a utf string and blows up b/c the conversion does not work, as it creates a nil value):
func buildFilePushRequest(fromUrl url: URL, httpBody: Data? = nil, fileName: String) -> NSMutableURLRequest? {
let cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
let request = NSMutableURLRequest(url: url, cachePolicy: cachePolicy, timeoutInterval: 2.0)
request.httpMethod = "POST"
// Set Content-Type in HTTP header.
let boundaryConstant = "Boundary-7MA4YWxkTLLu0UIW"; // This should be auto-generated.
let contentType = "multipart/form-data; boundary=" + boundaryConstant
let fileName = fileName
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.setValue("keep-alive", forHTTPHeaderField: "Connection")
var dataString = "--\(boundaryConstant)\r\n"
dataString += "Content-Disposition: form-data; name=file; filename=\"\(fileName)\"\r\n"
dataString += "Content-Type: octet-stream\r\n\r\n"
dataString += String(data: httpBody!, encoding: .utf8)! <-- CRASHES HERE
dataString += "\r\n"
dataString += "--\(boundaryConstant)--\r\n"
print("dataString: \(dataString)")
let requestBodyData = dataString.data(using: .utf8)
request.httpBody = requestBodyData
return request
}
I've read a bit that base64 is a better way to go rather than utf8 for binary type data, but I'm not really sure how best to modify the code above to use that instead, or if there is a better solution? Thanks in advance!
If the httpBody parameter can contain binary data you could compound the entire http body as Data
First of all please use native URLRequest, it's mutable as variable and why is the return value optional?
func buildFilePushRequest(fromUrl url: URL, httpBody: Data? = nil, fileName: String) -> URLRequest {
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 2.0)
...
var data = Data("--\(boundaryConstant)\r\n".utf8)
data += Data("Content-Disposition: form-data; name=file; filename=\"\(fileName)\"\r\n".utf8)
data += Data("Content-Type: octet-stream\r\n\r\n".utf8)
if let body = httpBody { data += body }
data += Data("\r\n".utf8)
data += Data("--\(boundaryConstant)--\r\n".utf8)
request.httpBody = data
I have the following swift code that submits a POST request successfully.
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.HTTPBody = "foo=bar&baz=lee".dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request, completionHandler: completionHandler)
Instead of using query parameter like syntax, I'd like to use a dictionary, but when I do the following:
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(["foo":"bar", "lee":"baz"], options: [])
let task = session.dataTaskWithRequest(request, completionHandler: completionHandler)
(like what I've seen around), it seems to submits the request as if the body is empty.
My main question is: how do these syntaxes differ, and how do the resulting requests differ?
NOTE: Coming from JS, I'm testing the endpoint in a javascript environment (jquery.com's console) like the following, and it's working successfully:
$.ajax({
url: url,
method: 'POST',
data: {
foo: 'bar',
baz: 'lee'
}
});
What #mrkbxt said. It's not a matter of how the syntax differs, but a matter of the different data types your sending with your request. UTF8 string encoded text is the default value for NSMutableURLRequest content type, which is why your first request works. To use a JSON in the body you have to switch the content type to use JSON.
Add the following to your request object so it accepts JSON:
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
For a content-Type of type "x-www-form-urlencoded" you can do the following.
let bodyParameter = ["foo":"bar", "lee":"baz"]
let bodyString = bodyData.map { "\($0)=\($1)" }.joined(separator: "&")
let encodedData = NSMutableData(data: bodyString.data(using: String.Encoding.utf8)!)
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.HTTPBody
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let task = session.dataTaskWithRequest(request, completionHandler: completionHandler)
This will take your dictionary, tranform it into a string that conforms to the content-Type of your request (by joining the dictionary using a separator) and then encoding it using utf8.
I send the same HTTP message from a HTTP proxy client and with NSURLRequest + NSURLConnection, and get back different result. It is an authentication request. From HTTP proxy authentication request is accepted, sending from app not. Why? Accepted means after redirection HTML will contains no Oops substring.
let url = NSURL(string: "http://www.swisshttp.weact.ch/en/user/login")
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let email2 = (viewController!.email.text as NSString).stringByReplacingOccurrencesOfString("#", withString: "%40")
let str = "name=\(email2)&pass=\(viewController!.password.text)&form_id=user_login" as NSString
let d = str.dataUsingEncoding(NSUTF8StringEncoding)
if let d2 = d {
request.HTTPBody = d2
let urlConnection = NSURLConnection(request: request, delegate: self)
}
UPDATE
I have put #teamnorge's code below into playground and into an empty Single View Application project. Returned HTML in project contains the Oops substring, code used in playground not containes it, any idea what is going on, why same request produce different HTML result? I get failed message also from iOS device and from simulator too.
UPDATE
Removed NSURLRequest cache like here recommended, but still not works as expected. And here.
UPDATE
Tried to remove all the credentials like here, but didn't help, no credential was found.
It looks like when you receive HTTP 302 and new Location URL, iOS does automatically fetch the page by this URL, so I guess your response is in fact the HTML content of the redirection page. Please verify.
UPDATE:
import UIKit
import XCPlayground
let url = NSURL(string: "http://www.swisshttp.weact.ch/en/user/login")
let request = NSMutableURLRequest(URL: url!)
let str = "name=kukodajanos%40icloud.com&pass=jelszo&form_id=user_login" as NSString
let d = str.dataUsingEncoding(NSUTF8StringEncoding)
request.HTTPBody = d
request.HTTPMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.currentQueue()) { response, maybeData, error in
if let data = maybeData {
let contents = NSString(data:data, encoding:NSUTF8StringEncoding)
println(contents)
if contents!.rangeOfString("Oops").length == 0 {
println("success")
} else {
println("failed")
}
} else {
println(error.localizedDescription)
}
}
XCPSetExecutionShouldContinueIndefinitely()
i have created a HTTP POST request in swift ios. when i fetch a post variables in php file, it is showing blank, i am not getting a variable value. below my swift code, look it.
var dataString = "name=john&type=student"
var request : NSMutableURLRequest = NSMutableURLRequest()
request.URL = NSURL(string: "http://www.example.com/example.php")
var postString = (dataString as NSString).dataUsingEncoding(NSUTF8StringEncoding)
request.HTTPMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.HTTPBody = postString
var connection = NSURLConnection(request: request, delegate: self, startImmediately: false)
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
//New request so we need to clear the data object
println(response)
}
Php code:
<?php
echo $_POST['name'];
?>
Anyone can suggest what should i do for getting a variables value. Thanks
You should use a Content-Type of application/x-www-form-urlencoded instead of application/json