HTTP Post Request data could not be read Swift 3 - ios

I've been trying to get data by Http "POST" method.In my php script i have a key call "categoryWise" which has a value called "flower".I put all the necessary codes but it doesn't work and says The data couldn’t be read because it isn’t in the correct format.Please help.
let values = "categoryWise= nature"
let parameter = values.data(using: .utf8)
let url = "https://mahadehasancom.000webhostapp.com/WallpaperApp/php_scripts/getImageByCategory.php"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "POST"
request.httpBody = parameter
request.setValue("application/x-content-type-options", forHTTPHeaderField: "Content-Type")
request.setValue("application/x-content-type-options", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if (error != nil)
{
print(error!)
}
else
{
do
{
let fetchData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
//print(fetchData)
let actorArray = fetchData?["result"] as? NSArray
for actor in actorArray!
{
let nameDict = actor as? NSDictionary
let name = nameDict?["date"] as! String
let countryname = nameDict?["category"] as! String
let imageUrl = nameDict?["url"] as! String
//let pageUrl = nameDict?["url"] as! String
authorArray.append(name)
titleArray.append(countryname)
imageURL.append(imageUrl)
//urlArray.append(pageUrl)
}
DispatchQueue.main.async {
self.CountryNameTable.reloadData()
}
print(authorArray)
print(titleArray)
print(imageURL)
print(urlArray)
}
catch let Error2
{
print(Error2.localizedDescription)
if let string = String(data: data!, encoding: .utf8)
{
print(string)
print(response!)
}
}
}
}
task.resume()

A few observations:
You shared PHP that is using $_POST. That means it's expecting x-www-form-urlencoded request. So, in Swift, you should set Content-Type of the request to be application/x-www-form-urlencoded because that's what you're sending. Likewise, in Swift, the Accept of the request should be application/json because your code will "accept" (or expect) a JSON response.
The values string you've supplied has a space in it. There can be no spaces in the key-value pairs that you send in a x-www-form-urlencoded request. (Note, if you have any non-alphanumeric characters in your values key pairs, you should be percent encoding them.)
In your Swift error handler, in addition to printing the error, you might want to try converting the data to a String, and looking to see what it says, e.g.
if let string = String(data: data!, encoding: .utf8) {
print(string)
}
You might also want to look at response and see what statusCode it reported. Having done that, you subsequently told us that it reported a statusCode of 500.
Status code 500 means that there was some internal error in the web service. (The code is 200 if successful.) This is generally a result of some error with the request not being handled correctly. For example, if the request neglected to format the request correctly and the web service doesn't anticipate/catch that, or if there was some other internal error on the web server, you could get 500 error code. For list of status codes, see http://w3.org/Protocols/rfc2616/rfc2616-sec10.html.
If the text in the body of the response from your web service is not illuminating, you might want to turn on error reporting (see How to get useful error messages in PHP? or How do I catch a PHP Fatal Error) and then look at the body of the response again. For example, you might include the following in your PHP:
<?php
function __fatalHandler() {
$error = error_get_last();
//check if it's a core/fatal error, otherwise it's a normal shutdown
if ($error !== NULL && in_array($error['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING))) {
header("Content-Type: application/json");
$result = Array("success" => false, "error" => $error);
echo json_encode($result);
die;
}
}
register_shutdown_function('__fatalHandler');
// the rest of your PHP here
?>

Related

http request delete and put

Below is my code for HTTP request for getting and post. What I wanted to know is how to do HTTP request for delete and put. I have made it possible for get and post to work. I want to know how on the part of delete and put based on my code below. what to change in my postcode when I want to change it to delete? what is lacking? I wanted to delete using ID
like for example "id": 16,
let parameters = ["name": "test", "desc": "test" , "reward":"1.00" , "sched":"2018-04-05T11:49:51+08:00", "occurrence":["name": "once"]
, "status": "created", "created_by": "test#gmail.com","created_for": "test.com"] as [String : Any]
guard let url = URL(string: "http://test.tesst.eu:8000/api/v1/test/") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else { return }
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
get
guard let url = URL(string: "http://test.test:8000/api/v1/test") else { return }
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
The only one that differs is GET with the parameters are in the url itself , POST & DELETE & PUT are work the same way , just change the httpMethod parameter and specify httpBody if you wanted to , meaning
DELETE : means delete a resource from a specific url
PUT : place a resource in to a web server
//
let parameters = ["ID": "16"] as [String : Any]
guard let url = URL(string: "http://test.tesst.eu:8000/api/v1/test/") else { return }
var request = URLRequest(url: url)
request.httpMethod = "DELETE"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject:parameters, options: []) else { return }
request.httpBody = httpBody
You should go through difference between each method types. It will help you, what should you do and when.
PUT
Store an entity at a URI. PUT can create a new entity or update an existing one. A PUT request is idempotent. Idempotency is the main difference between the expectations of PUT versus a POST request.
The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI. If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response. If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate successful completion of the request. If the resource could not be created or modified with the Request-URI, an appropriate error response SHOULD be given that reflects the nature of the problem. The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it does not understand or implement and MUST return a 501 (Not Implemented) response in such cases.
Modify the address with an ID of 1:
PUT /addresses/1
Note: PUT replaces an existing entity. If only a subset of data elements are provided, the rest will be replaced with empty or null.
urlRequestInstance.httpMethod = "PUT"
DELETE
Request that a resource be removed; however, the resource does not have to be removed immediately. It could be an asynchronous or long-running request.
The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY be overridden by human intervention (or other means) on the origin server. The client cannot be guaranteed that the operation has been carried out, even if the status code returned from the origin server indicates that the action has been completed successfully. However, the server SHOULD NOT indicate success unless, at the time the response is given, it intends to delete the resource or move it to an inaccessible location.
A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, 202 (Accepted) if the action has not yet been enacted, or 204 (No Content) if the action has been enacted but the response does not include an entity.
Delete an address with an ID of 1:
DELETE /addresses/1
urlRequestInstance.httpMethod = "DELETE"
Here are nice tutorial references for you:
Understanding REST
REST Methods
What is the usefulness of PUT and DELETE HTTP request methods?
Now you can try a sample code, answered by Sh_Khan

Getting token from an asp.net web api in iOS / swift

I am a .net developer but very new to iOS and swift development, just need help with consuming Web API using Swift2
The Asp.net Web API has been built with OAuth2 authentication, published to my Azure VM server with SSL certificate installed. The API site itself works properly, tested through Postman
However I got stuck when started writing first few lines of code in Swift trying to get Authentication token. After reading some online tutorials I decided to engage Alamofire, and produced below codes snippet:
func GetToken() {
let params = [
"grant_type" : "password",
"username" : "123456#qq.com",
"password" : "averygoodpassword"
]
let headers = [
"Content-Type" : "application/x-www-form-urlencoded"
]
request(.POST, "https://api.example.com/token",
parameters: params,
headers: headers,
encoding: .JSON)
.responseJSON { request, response, result in
print (request)
print (response?.description)
print (result)
switch result {
case .Success(let JSON):
print("Success with JSON: \(JSON)")
case .Failure(let data, let error):
print("Request failed with error: \(error)")
if let data = data {
print("Response data: \(NSString(data: data, encoding: NSUTF8StringEncoding)!)")
}
}
}
}
It ends up with below output in Xcode which didn't seem to be OK. The error = unsupported_grant_type told me that the request were sent to server but the parameters were not sent with request properly. I really cannot figure out the reason and solution, had been digging on Internet for a few days but still feeling desperate with it. Can anyone help please? Even if someone can provide a pure swift solution without any 3rd party library will be greatly helpful. Thanks!
Xcode output:
Optional( { URL: https://api.example.com/token })
Optional(" { URL: https://api.example.com/token } { status code: 400, headers {\n \"Access-Control-Allow-Headers\" = \"Content-Type\";\n \"Access-Control-Allow-Methods\" = \"GET, POST, PUT, DELETE, OPTIONS\";\n \"Access-Control-Allow-Origin\" = \"*\";\n \"Cache-Control\" = \"no-cache\";\n \"Content-Length\" = 34;\n \"Content-Type\" = \"application/json;charset=UTF-8\";\n Date = \"Fri, 30 Sep 2016 10:30:31 GMT\";\n Expires = \"-1\";\n Pragma = \"no-cache\";\n Server = \"Microsoft-IIS/8.5\";\n \"X-Powered-By\" = \"ASP.NET\";\n} }")
SUCCESS
Success with JSON: {
error = "unsupported_grant_type";
}
I had a similar problem trying to POST to MailGun for some automated emails I was implementing in an app.
I was able to get this working properly with a large HTTP response. I put the full path into Keys.plist so that I can upload my code to github and broke out some of the arguments into variables so I can have them programmatically set later down the road.
// Email the FBO with desired information
// Parse our Keys.plist so we can use our path
var keys: NSDictionary?
if let path = NSBundle.mainBundle().pathForResource("Keys", ofType: "plist") {
keys = NSDictionary(contentsOfFile: path)
}
if let dict = keys {
// variablize our https path with API key, recipient and message text
let mailgunAPIPath = dict["mailgunAPIPath"] as? String
let emailRecipient = "bar#foo.com"
let emailMessage = "Testing%20email%20sender%20variables"
// Create a session and fill it with our request
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: mailgunAPIPath! + "from=FBOGo%20Reservation%20%3Cscheduler#<my domain>.com%3E&to=reservations#<my domain>.com&to=\(emailRecipient)&subject=A%20New%20Reservation%21&text=\(emailMessage)")!)
// POST and report back with any errors and response codes
request.HTTPMethod = "POST"
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.URL!)")
print("response = \(response)")
let httpResponse = response as! NSHTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
The Mailgun Path is in Keys.plist as a string called mailgunAPIPath with the value:
https://API:key-<my key>#api.mailgun.net/v3/<my domain>.com/messages?
I'm slightly opposed to using 3rd party libraries, especially for small things like a http POST and this seems like a much more maintainable solution to me. Anyways, hope this helps, let me know if you have any questions!

iOS swift post request with binary body

I want to make a POST request from iOS (swift3) which passes a chunk of raw bytes as the body. I had done some experimenting which made me thought the following worked:
let url = URL(string: "https://bla/foo/bar")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = Data(hex: "600DF00D")
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response, error) in
"DATA \(data ?? Data()) RESPONSE \(response) ERROR \(error)".print()
}
task.resume()
Didn't know it was a problem until I tried sending something simple like a single 0xF0. At which point my tornado server started complaining that I was sending it
WARNING:tornado.general:Invalid x-www-form-urlencoded body: 'utf-8' codec can't decode byte 0xf0 in position 2: invalid continuation byte
Am I just supposed to set some header somehow? Or is there something different I need to do?
The two common solutions are:
Your error message tells us that the web service is expecting a x-www-form-urlencoded request (e.g. key=value) and in for the value, you can perform a base-64 encoding of the binary payload.
Unfortunately, base-64 strings still need to be percent escaped (because web servers generally parse + characters as spaces), so you have to do something like:
let base64Encoded = data
.base64EncodedString(options: [])
.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)!
.data(using: String.Encoding.utf8)!
var body = "key=".data(using: .utf8)!
body.append(base64Encoded)
var request = URLRequest(url: url)
request.httpBody = body
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
print(error!)
return
}
...
}
task.resume()
Where:
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
}()
}
For more discussion on that character set, see point 2 in this answer: https://stackoverflow.com/a/35912606/1271826.
Anyway, when you receive this on your server, you can retrieve it as and then reverse the base-64 encoding, and you'll have your original binary payload.
Alternatively, you can use multipart/formdata request (in which you can supply binary payload, but you have to wrap it in as part of the broader multipart/formdata format). See https://stackoverflow.com/a/26163136/1271826 if you want to do this yourself.
For both of these approaches, libraries like Alamofire make it even easier, getting you out of the weeds of constructing these requests.

NSURLSession.sharedSession().dataTaskWithRequest(request) not returning anything unless there is an error

Why aren't any results being printed?
I have been searching all over for an answer. I've tried many different example blocks of code. The print statements never fires unless an error is produced. For example: If I change the URL to "http" only with nothing else, I naturally get an error and it prints the error. However, any valid URL produces no result in the print statement.
func post()
{
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.thebeerstore.ca")!)
request.HTTPMethod = "POST"
let postString = "experiment"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString!)")
}
task.resume()
}
Edit: It works if I use a playground, but only in a playground.
In your application's Info.plist file, add the property App Transport Security Settings and under that, add Allow Arbitrary Loads and assign YES for it.
With the above settings in your Info.plist, the app should be able to load your http:// URLs as well
Note: I would recommend not to use the above settings in your
production build, it could result in security issues.
There is no good explanation for this, but having finally stuck this same problem code into my primary project, it now works perfectly ok. What changed??? Arrrrg! How many hours wasted and I still don't really know why it wasn't working outside of my primary app. Thanks for your help everyone.
Final code:
I do hope I can send over 100MB through a post request to be interpreted on the server side and store to the database. Haven't done this before. I'm a newbie.
func post()
{
let request = NSMutableURLRequest(URL: NSURL(string: "http://bruceexpress.com/database.php")!)
request.HTTPMethod = "POST"
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(stores, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString!)")
}
task.resume()
}
Edit: Yes, you can post a huge block of data. Is this the correct way to do it? ( Forgive my silly questions, but I am recovering programming skills left over from 2001. I am a very outdated individual. )
Basically, what I have done is read all of the data on http://www.thebeerstore.ca, extracted every beer and its info and prices, and extracted every store and its info. I interpreted this data, converted it into a large JSON block, and sent it to my server to be interpreted by a php script which will store this data to a database.
Sound like the right thing to do, or is there a better way to fill the database?

Swift POST request delivers body to node.js/express application in wrong format

I am trying to send a JSON object from an ios app using Swift. Everything works but the fact that the body in the request body on the node.js/express backend is in an aca-awkward format where the whole JSON object parsed in Swift is the actual key. So what is being received on the server is:
req.body = { '{"email":"email","password":"password"}': '' }
I want it to be:
{ "email":"email","password":"password" }
So I can access the key-values with, for example, req.body.email
I am new to http communication between ios and nodejs so maybe this is
normal but its very annoying.
My ios http post code is :
let url = NSURL(string: "http://localhost:3000/users")
let request = NSMutableURLRequest(URL:url!)
request.HTTPMethod = "POST"
var login_details: [String: AnyObject] = [
"email" : "\(self.email_field.text)",
"password" : "\(self.password_field.text)"
]
let valid = NSJSONSerialization.isValidJSONObject(login_details)
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(login_details, options: nil, error: &err)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
}
My node router module is:
router.post('/', function(req, res) {
console.log(req.body);
});
Node automatically parses the JSON request body to object only if it knows that it is the right content type. You have two possible solutions, use one or the other but not both since they conflict.
1) client side, more correct IMO. Set the right content type. Add this to your Swift code:
request.setValue("application/json" forHTTPHeaderField:"Content-Type")
2) server side. Just parse the body yourself:
parsedBody = JSON.parse(body)
// parsedBody will now be an object

Resources