I'm having an issue getting my JSON object in a proper format on my server running Node.js. I have my API set up to receive a JSON object and store it in a database. This works great when I send it a POST request from postman (as shown below), but it throws an odd error when I send it from iOS. As an example:
The first json object shown is sent from postman. The second one is from iOS (using Swift 3). The error essentially crashes the server, saying:
AssertionError: Error: key {"firstname":"testFirstName","lastname":"testLastName","email":"testemail123#gmail.com","username":"testusername123", == null
I'm not exactly sure why this is the case. I assume it has to do with the way the object is created? This is the swift code I'm using to create the object:
let infoDictionary = [
"username": UserNameField.text!,
"password": PasswordField.text!,
"firstname": FirstNameField.text!,
"lastname": LastNameField.text!,
"email": EmailField.text!
]
// Whole block = send above dictionary as JSON to server:
if JSONSerialization.isValidJSONObject(infoDictionary) {
do {
let jsonObject = try JSONSerialization.data(withJSONObject: infoDictionary,
options: .prettyPrinted)
I then append that to the http body. Note that the fields are from text fields in the UI of the app that I unwrap and place into a dictionary. When I print it out as a string in swift, it comes out as the correct format, there is just something going wrong when I actually go to send it to my server.
EDIT: Per request the full URL request is below:
do {
let jsonObject = try JSONSerialization.data(withJSONObject: infoDictionary,
options: .prettyPrinted)
// Create Post request
let url = URL(string: "websiteurl")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
// Append JSON object
request.httpBody = jsonObject
// Send request
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No Data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
}
}
task.resume() // Sends the request
} catch {
print(error.localizedDescription)
}
}
The request body is fine, it contains a valid JSON object, but
apparently the server interprets the HTTP body as string and wraps
it into another JSON object. Note that "text/plain" is the default
content type for HTTP requests (https://www.rfc-editor.org/rfc/rfc2045#section-5.2).
The solution is to set the content type explicitly:
request.addValue("application/json", forHTTPHeaderField: "Content-type")
Related
Hai I am trying to pass some parameters of string in Http post request. I have created a dictionary and then converted that dictionary to data and set as httpBody.But when I looked on our server nothing has been passd I mean parameters are empty.Why? What mistake i am doing?Please help me to find out.Thanks in advance.
func receiptValidation(productId:String,requestFrom:String)
{
let SUBSCRIPTION_SECRET = "mySecretKey"
let defaults = UserDefaults.standard
let receiptPath = Bundle.main.appStoreReceiptURL?.path
if FileManager.default.fileExists(atPath: receiptPath!){
var receiptData:NSData?
do {
receiptData = try NSData(contentsOf: Bundle.main.appStoreReceiptURL!, options: NSData.ReadingOptions.alwaysMapped)
}
catch{
print("ERROR: " + error.localizedDescription)
}
//let receiptString = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
let base64encodedReceipt = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions.endLineWithCarriageReturn)
print(base64encodedReceipt!)
let requestDictionary = ["receipt-data":base64encodedReceipt!,"password":SUBSCRIPTION_SECRET]
guard JSONSerialization.isValidJSONObject(requestDictionary) else { print("requestDictionary is not valid JSON"); return }
do {
let requestData = try JSONSerialization.data(withJSONObject: requestDictionary)
let requestDataString=String(describing: requestData)
let URLForApplication:String = String(format:"%#/api/validate-receipt-data",opcodeDetails["apiProxyBaseUrl"]!) // this works but as noted above it's best to use your own trusted server
SwiftyBeaver.info("URLForApplication Path:\n\(URLForApplication)")
let url:URL! = URL.init(string: URLForApplication)
var request = URLRequest.init(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let configure = URLSessionConfiguration.background(withIdentifier: Bundle.main.bundleIdentifier!)
session1=URLSession(configuration: .default, delegate: applicationDelegate.application, delegateQueue: OperationQueue.main)
var postString =
["receiptData":requestDataString,
"deviceType":"IOS",
"subscriberId":encodeString(normalString: defaults.array(forKey: "userDetails")?.first as! String),
"password":encodeString(normalString: defaults.array(forKey: "userDetails")?.last as! String),
"productId":encodeString(normalString: productId ),
"code":opcodeDetails["opCode"]!]
do {
request.httpBody = try JSONSerialization.data(withJSONObject: postString, options: .prettyPrinted) // pass dictionary to nsdata object and set it as request body
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let task = session1?.dataTask(with: request) { (data, response, error) in
if let data = data , error == nil {
do {
let appReceiptJSON = try JSONSerialization.jsonObject(with: data)
print("success. here is the json representation of the app receipt: \(appReceiptJSON)")
// if you are using your server this will be a json representation of whatever your server provided
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
} else {
print("the upload task returned an error: \(error)")
}
}
task?.resume()
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
}
}
and what error i am getting is Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
You don’t say, but I’m assuming you’re getting this error where you print “json serialization failed with error”. If so, CZ54 is right, that your response is obviously not JSON. So, where you print that error, also print the header and body to see what the server actually returned, if anything:
print("response header:", response ?? "No response")
print("response body:", String(data: data, using: .utf8) ?? "No body")
The response header will include the status code (which should be something in the 200...299 range). If it’s not in that range, the status code will tell you the broad nature of the problem.
And regarding the response body, Sometimes (esp in development environments) if the server choked on something, it may return a HTML page outlining the nature of the problem (though, admittedly, in other cases, it only outputs the fact that there was an error, but not the details, and you’ll need to go into the server error logs to figure out what went wrong).
Looking at the specifics of the response, like above, is is your first step. Or you can achieve this by running the app on a simulator, and watching the request and the response in a tool like Charles or Wireshark. Once you get them up and running, these are great tools for inspecting requests and responses.
The next question is why the server generated the response that it did. As a general rule, while these sorts of problems can be a result of some server mistake, the more likely scenario is that the request wasn’t formed correctly and the server didn’t know how to handle it. Looking at the response (or looking at your server’s error logs) often provides good clues. But there’s no way anyone can help you on the basis of the information provided.
I am getting the following error when performing the request below.
Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start
with array or object and option to allow fragments not set."
UserInfo={NSDebugDescription=JSON text did not start with array or
object and option to allow fragments not set.}
#IBAction func onPostTapped(_ sender: Any) {
let parameters = ["Name": "Yogesh", "Mobile": "1212121212", "DOB": "1122/12/12", "Address": "qwqwqwqw"]
//https://jsonplaceholder.typicode.com/posts
guard let url = URL(string: "http://localhost/webservice/Register.php") 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()
}
What could be causing this issue?
The error explains the issue, the JSON received doesn't start with an object or an array and Allow Fragments is not set.
Check your JSON is what you would expect to receive
You can enable allow fragments like so..
try JSONSerialization.jsonObject(with: data, options: .allowFragments)
Allow fragments allows you to load partial JSON data that does not map directly to an array or Dictionary
From the docs:
Specifies that the parser should allow top-level objects that are not
an instance of NSArray or NSDictionary.
Here's the code:
func makePOSTCall(endpoint: String, languageName: String) {
guard let url = URL(string: endpoint) else {
print("Could not create URL.")
return
}
let requestLang: [String: Any] = ["name": languageName]
let requestBody = try? JSONSerialization.data(withJSONObject: requestLang)
var urlRequest = URLRequest(url: url)
urlRequest.httpBody = requestBody
urlRequest.httpMethod = "POST"
let session = URLSession.shared
let task = session.dataTask(with: urlRequest) {
data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
}
}
task.resume()
}
This sends a {"name": "Go"} JSON dictionary to Flask. Flask is supposed to append the language name to an array and return the full array in the response. Now, this works when I send the request manually, so it's not Flask's error. But when I send the above from iOS, I get request.json == None in the flask console. Clearly, I'm sending an empty body, but I shouldn't be. Any idea where I went wrong?
I call the function as
#IBAction func pressedMakePOSTCall(_ sender: UIButton) {
makePOSTCall(endpoint: "http://127.0.0.1:5000/lang", languageName: "Go")
}
I tried adding a trailing slash, just get a 404 in the console. The only question similar to mine that I've found is this: How to make HTTP Post request with JSON body in Swift and my code is basically identical.
#weissja19 was correct, I needed to set content type to application/json. Adding
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.setValue("application/json", forHTTPHeaderField: "Accept")
fixed the error. Now the code works as I expected.
P.S. I couldn't catch it because I use the app Paw for testing, which sets content type automatically.
You might want to do it manually:
urlRequest.httpBody = "name=\(languageName)".data(using: .utf8)
Use JSONSerialization will make your POST body like {"name":"abc"} which might not be supported by your server
I am trying to create a login system for my iOS mobile application. I have a request sending to my Node.js server with Swift 3 with:
#IBAction func loginBtn(_ sender: UIButton) {
//created NSURL
let requestURL = NSURL(string: loginURL)
//creating NSMutableURLRequest
let request = NSMutableURLRequest(url: requestURL! as URL)
//setting the method to post
request.httpMethod = "POST"
//getting values from text fields
let empNum = empTextField.text
let empPass = passTextField.text
//creating the post parameter by concatenating the keys and values from text field
let postParameters = "empNum=" + empNum! + "&empPass=" + empPass!;
//adding the parameters to request body
request.httpBody = postParameters.data(using: String.Encoding.utf8)
//creating a task to send the post request
let task = URLSession.shared.dataTask(with: request as URLRequest){
data, response, error in
if error != nil{
print("error is \(String(describing: error))")
return;
}
//parsing the response
do {
//converting resonse to NSDictionary
let myJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
print("myjson : \(String(describing: myJSON))")
//parsing the json
if let parseJSON = myJSON {
//creating a string
var msg : String!
//getting the json response
msg = parseJSON["message"] as! String?
//printing the response
print("message here: \(msg)")
}
} catch {
print("Error here: \(error)")
}
}
//executing the task
task.resume()
}
I am getting a 200 server side but I want to be able to res.send a string back to Swift so I can proceed to take further actions. How do I check that response? Like:
if response == "vaild" {
...
}else{
...
}
When I run this code I get an error that reads:
Error here: Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
P.S. I am also not really familiar with the request code so if it is right in front of my face please just explain the code to me then. Sorry in advance!!
Your code sample suggests that your client expects a json response with a property message in it. If you are using resp.send('Success') from your server it will not be a json object. It will be a string.
If that is what you want on your server response you should update your client to simply parse the data to a string. You can use String(data: data, encoding: .utf8) where "data" is what is returned from the response.
However, I would recommend leveraging HTTP Status codes. In your server code you can respond with a 422 if login was not successful. This can be accomplished with resp.status(422).send('Some optional message'). Then client side all you need to do is check the response status. Instead of a string compare.
I am getting this error as JSON result.error. While my JSON is an valid one, check it on JSON vaildator online.
This is my code for JSON request.
Alamofire.request(.POST, url, parameters: parameters, encoding:.JSON)
.responseJSON { (request, response, result) in
hud.hide(true)
// Set flag to disale poor internet connection alert
weakInternet = false
print(result.error)
if (result.value != nil) {
print("API Response: \(result.value!)")
// Pass the response JSON to the completion block
completion(json: result.value!)
} else {
// Response JSON is NULL
}
}
When i hit the same service with particular request parameters i am getting this response.
{"error":"success","post_data":{"first_name":"hd","last_name":"df","email":"hiiaaaaaaa#dnsa.coma","password":"himanshu","confirm_password":"himanshu","companies":["Big Rattle Technologies_Fg_yes"],"institutes":[""]},"msg":"success","data":{"_id":"5742ae1564b35e37369f0838","first_name":"hd","last_name":"df","email":"hiiaaaaaaa#dnsa.coma","profile_pic":"","loc":[0,0],"locs":{"type":"Point","coordinates":[0,0]},"institutes":[],"companies":[{"comapny_id":"555b2d0a678e79ed510041ce","group_id":"556c2434678e79a1820041dd","name":"Big Rattle Technologies","designation":"Fg","is_current":"yes"}],"device_token":"","user_group":"site_user","is_verified":0,"is_disclose":1,"is_discover":1,"wallNotification":1,"messageNotification":1,"matchNotification":1,"verificationSent":0,"status":1,"mobile":"","linkedin_id":"","facebook_id":"","os":"","qblox_id":12957726,"updated_at":"2016-05-23 07:15:33","created_at":"2016-05-23 07:15:33","current_company":"Big Rattle Technologies"}}
Anybody knows what is the problem in my case?
There is an issue with my web service. They are giving me the response in "text/HTML" format rather than HTML. When i printed my response on debugger then i got:
"Content-Type" = "text/html; charset=UTF-8";
Now, i updated my webservice and everything is working like a charm.
I am getting same error last time because there will be problem is web service returns me response in array and i am trying to convert its into dictionary and extract its value.
Check Your web service response.
Swift 5, Swift 4
var headers = HTTPHeaders()
headers = [
"Content-Type" :"text/html; charset=UTF-8",
//"Content-Type": "application/json",
//"Content-Type": "application/x-www-form-urlencoded",
//"Accept": "application/json",
"Accept": "multipart/form-data"
]
I got this error when using the wrong Firebase Server key to send a remote push notification.
Go to Firebase > Project Overview > CogIcon > Project Settings > Cloud Messaging > Server Key
guard let url = URL(string: "https://fcm.googleapis.com/fcm/send") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = try?
let paramDict = [String:Any]() // this has values
JSONSerialization.data(withJSONObject: paramDict, options: [])
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let serverKey = "firebase server key" // *** SERVER KEY USED HERE ***
request.setValue("key=\(serverKey)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
if let jsonData = data {
if let jsonDataDict = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject] {
print("jsonData Successfully Sent data: \n\(jsonDataDict))")
}
}
} catch let err as NSError {
print("jsonData Error: \(err.debugDescription)")
}
}
task.resume()
Make Sure The firewall Off from Server