This question already has answers here:
Cocoa Error 3840 when POST Request with AFNetworking 2
(4 answers)
Closed 7 years ago.
I'm running into a service error as I try to POST some data to a server. I'm confident the error has something to do with my request structure. I'm using AFNetworking and my request looks as follows:
lazy var networkManager: AFHTTPRequestOperationManager = {
var networkManager : AFHTTPRequestOperationManager = AFHTTPRequestOperationManager()
networkManager.requestSerializer.setValue("application/json",
forHTTPHeaderField: "Content-Type")
networkManager.responseSerializer.acceptableContentTypes =
NSSet(array: ["text/plain", "text/html", "application/json"]) as Set<NSObject>
return networkManager
}()
func createNewAccount(username:String, password:String, onCompletion:Closures.ServiceResponse) -> Void{
let parameters: [String: AnyObject] = [
"newUser": [
"name": username,
"password": password
]
]
var err : NSError?
var jsonData = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: &err)
var jsonString = NSString(data: jsonData!, encoding: NSUTF8StringEncoding)
networkManager.POST(Constants.STAGING_URL + "/Services/UserService/UserService.svc/NewUserSignup",
parameters: jsonString,
success: { (operation: AFHTTPRequestOperation!,responseObject: AnyObject!) in
println(responseObject)
},
failure: { (operation: AFHTTPRequestOperation!,error: NSError!) in
println(error.localizedDescription)
println(operation.responseString)
})
}
I've tried variations on the parameters option and I still run into the same problem.
Now here is the method I have written by hand that works just fine and sends data to the server as expected:
func signupNewUser(userName : String, password : String){
let jsonObject: [String: AnyObject] = [
"newUser": [
"name": userName,
"password": password
]
]
var urlString = Constants.STAGING_URL + "/Services/UserService/UserService.svc/NewUserSignup"
var request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(jsonObject, options: nil, error: &err)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var jsonData = NSJSONSerialization.dataWithJSONObject(jsonObject, options: nil, error: &err)
var jsonString = NSString(data: jsonData!, encoding: NSUTF8StringEncoding)
request.setValue(jsonString! as String, forHTTPHeaderField: "json")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println(response)
})
task.resume()
}
The above request works just fine and the server can process the incoming JSON and save it in the database successfully.
My server is a WCF service. The interface for the method I'm calling looks like this:
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest, ResponseFormat = WebMessageFormat.Json, UriTemplate = "/NewUserSignup")]
Response AddNewUser(User newUser);
The actual NSError is:
Printing description of error:
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (JSON text did not start with array or object and option to allow fragments not set.) UserInfo=0x7f98a0c58910 {NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set., NSUnderlyingError=0x7f98a0c582f0 "Request failed: bad request (400)"}
I assume the server is fine and it just has to do with configuring the request properly with AFNetworking. Any help configuring the request properly would be appreciated.
Solution in my case was simple and had to do with the configuration of the NetworkManager.
This worked for me as the NetWorkManager will be receiving JSON from the server:
lazy var networkManager: AFHTTPRequestOperationManager = {
var networkManager : AFHTTPRequestOperationManager = AFHTTPRequestOperationManager()
var jsonRequestSerializer = AFJSONRequestSerializer()
networkManager.requestSerializer = jsonRequestSerializer
return networkManager
}()
Related
I am trying to upload an image from my iOS app written in swift and tried some codes I found about it but nothing works.
Uploading throung Postman works perfect. Here a screenshot of the request in Postman:
Uploading image with Postman
As you can see the API expects a the PUT request with a JSON with only one field called "avatar" and then the image in it. Also keep in mind the form-data. In the Headers I only send the token to do the authentication.
Said this, in the Swift code I try to send as params the array with only the "avatar" key with the image encoded:
func uploadImage(url: String){
let httpMethod: String = "PUT"
var token = getTokenFromNSUserDefaults()
var imageDataJpeg = UIImageJPEGRepresentation(self.myImage, 0.9)
var base64StringJpeg = imageDataJpeg.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) // encode the image
var base64StringJpeg2 = imageDataJpeg.base64EncodedStringWithOptions(nil) // encode the image
var imageDataPng = UIImagePNGRepresentation(self.myImage)
var base64StringPng = imageDataPng.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) // encode the image
var base64StringPng2 = imageDataPng.base64EncodedStringWithOptions(nil) // encode the image
// API Request
self.request(httpMethod, token: token, params:["avatar":base64StringPng2], url: url) { (succeeded: Bool, msg: String) -> () in
var alert = UIAlertView(title: "Success!", message: msg, delegate: nil, cancelButtonTitle: "Okay.")
if(succeeded) {
println("Success")
}
else {
println("Fail")
}
// Move to the UI thread
dispatch_async(dispatch_get_main_queue(), { () -> Void in
})
}
}
//// API REQUEST ////
func request(httpMethod: String, token: String, params: Dictionary<String, AnyObject>, url : String, postCompleted : (succeeded: Bool, msg: String) -> ()) {
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = httpMethod
var err: NSError?
if token != ""{
request.addValue("JWT \(token)", forHTTPHeaderField: "Authorization")
}
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
////Option 1
request.addValue("multipart/form-data", forHTTPHeaderField: "Content-Type")
request.addValue("multipart/form-data", forHTTPHeaderField: "Content-Disposition")
request.addValue("multipart/form-data", forHTTPHeaderField: "Accept")
////Option 2
//request.addValue("multipart/form-data", forHTTPHeaderField: "Content-Type")
//request.addValue("multipart/form-data", forHTTPHeaderField: "Content-Disposition")
//request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println("Response: \(response)")
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Body: \(strData)")
var err: NSError?
var msg = "No message"
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &err)
println("JSON: \(json)")
if(err != nil) {
println(err!.localizedDescription)
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON1: '\(jsonStr)'")
postCompleted(succeeded: false, msg: "Error")
}
else {
self.parseJson(json!)
postCompleted(succeeded: true, msg: "Works!")
return
}
})
task.resume()
}
Here's the log response from the server with always a 406 error message as follows (do not look at the "censored" url):
Response: <NSHTTPURLResponse: 0x7f967bed1450> { URL: https://***********************/ } { status code: 406, headers {
Allow = "GET, PUT, DELETE, HEAD, OPTIONS";
Connection = "keep-alive";
"Content-Type" = "application/json";
Date = "Wed, 02 Dec 2015 18:30:13 GMT";
Server = "nginx/1.4.6 (Ubuntu)";
"Transfer-Encoding" = Identity;
Vary = Accept;
"X-Frame-Options" = SAMEORIGIN;
} }
Body: Optional()
JSON: nil
The operation couldn’t be completed. (Cocoa error 3840.)
Error could not parse JSON1: 'Optional()'
Fail
Thank you very much for your help in advance!
Your "params" is not in JSON format - the curly braces for JSON dictionary are missing.
I have problem when i post the data to server, it seems doesn't work at all.. I've found the code thats the other use it for post
here is the code for post
func post(params : Dictionary<String, String>, url : String, postCompleted : (succeeded: Bool, msg: String) -> ()) {
var request = NSMutableURLRequest(URL: NSURL(string: "")!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
// let postString = "Body: \(personaldata.data)"
var err: NSError?
// request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println("Response: \(response)")
var strData = NSString(CString: personaldata.data, encoding: NSUTF8StringEncoding)
println("Body: \(personaldata.data)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &err) as? NSDictionary
var msg = "No message"
})
task.resume()
}
and when the button send pressed, here is the code
func tapGesture(gesture: UIGestureRecognizer) {
self.performSegueWithIdentifier("analisa", sender: self)
if let Kirim = gesture.view as? UIImageView { // if you subclass UIImageView, then change "UIImageView" to your subclass
self.post(["ID":"123", "function_name":"update_db", "personal_information_table":"\(personaldata.data)"], url: "") { (succeeded: Bool, msg: String) -> () in
}
}
}
the output when println correct already,but it doesn't appear at the website, how it could be?
I hide the website address ( it only shows line with id and weight , height on it that contains in(personaldata.data)
well my friend who working on android, have sent the data successfully :/
here is the website, and that is my friends data, not mine
is my code error or what?
here is the println
Response: <NSHTTPURLResponse: 0x7fe591554d60> { URL: } { status code: 500, headers {
"Accept-Ranges" = none;
"Cache-Control" = private;
"Content-Type" = "text/plain; charset=UTF-8";
Date = "Fri, 28 Aug 2015 09:34:02 GMT";
Server = "Google Frontend";
Vary = "Accept-Encoding";
"alt-svc" = "quic=\":443\"; p=\"1\"; ma=604800";
} }
Body: {"height": 214, "weight": 123}
Status code: 500 indicates that the error can only be resolved by fixes to the Web server software. It is not a client-side problem. It is up to the operators of the Web server site to locate and analyse the logs which should give further information about the error.
I need to upload an image to the server endpoint where the structure has to be as following:
{ "image": { "file": imageData }, "access_token": access_token }
How can I send such a request using NSURLSession (or maybe even Alamofire or AFNetworking)?
You cannot just include binary image data in a JSON request. JSON requires text representation, so if you do this, you must convert it to string (e.g. base64 encoding), use that in the JSON, and then the server code would presumably convert the base64 string back to binary data before attempting to use it.
But if you were base64 encoding of the image, it might look something like:
// get image data
let imageData = UIImagePNGRepresentation(image)
// convert to base64
let base64String = imageData.base64EncodedStringWithOptions(nil)
// build parameters
let parameters = ["image": ["file" : base64String], "access_token" : accessToken]
// get JSON
var error: NSError?
let data = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: &error)
assert(data != nil, "Unable to serialize \(error)")
// build request
let url = NSURL(string: "http://example.com/upload")!
let request = NSMutableURLRequest(URL: url)
request.addValue("text/json", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
let task = NSURLSession.sharedSession().uploadTaskWithRequest(request, fromData: data) { data, response, error in
// check for basic connectivity errors
if error != nil {
println("error: \(error)")
return
}
// check for server errors
if let httpResponse = response as? NSHTTPURLResponse, let statusCode = httpResponse.statusCode as Int? {
if statusCode != 200 {
println("status code is \(statusCode)")
}
}
// check for details of app-level server response, e.g. if JSON that was dictionary:
var parseError: NSError?
if let responseObject = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError) as? [String : AnyObject] {
println(responseObject)
} else {
println("JSON parse failed: \(parseError)")
println("response was: \(response)")
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("responseString was: \(responseString)")
}
}
task.resume()
If you use Alamofire, this is simplified:
// build parameters
let parameters = ["image": ["file" : base64String], "access_token" : accessToken] as [String : AnyObject]
// build request
let urlString = "http://example.com/upload"
Alamofire.request(.POST, urlString, parameters: parameters, encoding: .JSON)
.responseJSON(options: nil) { request, response, responseObject, error in
if error != nil {
println(error)
} else {
println(responseObject)
}
}
But both of the above are making assumptions about the nature of the response, that the server is base64 decoding the image data from the JSON, etc., but hopefully this illustrates the basic patterns.
Alternatively, use an application/x-www-form-urlencoded request, in which you can send binary data as illustrated here.
Found a solution using AFNetworking with help from https://stackoverflow.com/a/11092052/3871476
For others looking for the solution.
let manager = AFHTTPRequestOperationManager(baseURL: NSURL(string: url))
let request = manager.POST(url, parameters: param, constructingBodyWithBlock: {(formData: AFMultipartFormData!) -> Void in
formData.appendPartWithFileData(imgdata, name: "image[file]", fileName: "photo.jpeg", mimeType: "image/jpeg")
}, success: {(operation: AFHTTPRequestOperation!, responseObject: AnyObject!) -> Void in
//Success
}, failure: {(operation: AFHTTPRequestOperation!, error: NSError!) -> Void in
//Failure
println(error.localizedDescription)
})
The trick was to use the "image[file]" parameter.
Try this:
var request = NSMutableURLRequest(URL: NSURL(string: "https://\(IP):\(port)/")!)
var response: NSURLResponse?
var error: NSError?
//Adding the JSON String in HTTP Body
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(jsonString, options: nil, error: &error)
request.timeoutInterval = (number as! NSTimeInterval)
request.HTTPMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("gzip", forHTTPHeaderField: "Accept-encoding")
let urlData = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
I need to send a post request to my server with HTTPBody of Array. Here's my array of parameters:
params = [
"message" : [
"alert" : "Find your iPhone",
"sound" : "Binocular_Default.caf"
]
]
Now I need to set NSMutableURLRequest's HTTPBody to this array. How can I do that?
Create mutable request with your params. and try with following code
var request = NSMutableURLRequest(URL: NSURL(string: "yoururl"))
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
//create dictionary with your parameters
var params = ["username":"test", "password":"pass"] as Dictionary<String, String>
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println("Response: \(response)")
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Body: \(strData)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &err) as? NSDictionary
// Did the JSONObjectWithData constructor return an error? If so, log the error to the console
if(err != nil) {
println(err!.localizedDescription)
}
else {
}
})
task.resume()
So Im in the early stages of learning swift, and I'm trying to make a trivial class to wrap the process of sending/retrieving data from a given web service. The issue I'm having is that nothing is printing to console after I have sent the request, or any kind of response for that matter. I would really appreciate any help or guidance as to what I am doing wrong
import Foundation
class URLHelper : NSObject,NSURLConnectionDelegate,NSURLConnectionDataDelegate{
var data = NSMutableData()
func sendReq(){
let urlPath: String = "http://localhost/web-service/action.php?callback=showUserDetails&uid=1"
var url: NSURL = NSURL(string: urlPath)!
var request: NSURLRequest = NSURLRequest(URL: url,cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData, timeoutInterval: 4)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)!
connection.start()
}
func connection(connection: NSURLConnection!, didReceiveData data: NSData!){
self.data.appendData(data)
}
func connection(connection: NSURLConnection, didFailWithError error: NSError) {
println(error.description)
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
var err: NSError
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
println(jsonResult)
}
}
var req = URLHelper()
req.sendReq()
UPDATE
<?php
//Get the action to run the coorect request
if(isset($_GET['callback'])){
$function = $_GET['callback'];
call_user_func($function);
//$function();
}else{
echo "Error: No valid callback supplied to request";
}
function showUserDetails(){
$conn = mysqli_connect("localhost", "root", "root", "service_db") or die("Error " . mysqli_error($conn));
$userid = $_GET['uid'];
$results = mysqli_fetch_assoc(mysqli_query($conn,"SELECT * FROM user WHERE id = $userid"));
mysqli_close($conn);
echo json_encode($results);
}
if($db->connect_errno > 0){
die('Unable to connect to database [' . $db->connect_error . ']');
}
?>
The returned json is as follows {"id":"1","username":"tom","email":"tom_smith#gmail.com"}
For posting you don't need to make a request, just get content from the url:
var strResult:NSString
let strURL = "http://localhost/web-service/action.php?callback=showUserDetails&uid=1"
var dataURL = NSData(contentsOfURL: NSURL(string: strURL)!);
if let d = dataURL
{
strResult = NSString(data: d, encoding: NSUTF8StringEncoding)!
println(strResult)
}
Here i simply go to the url, get the content and store it in the data object, and then turning it into a string and printing it. You can also decode the JSON string (strResult) into the JSON object.
Hope it helps :)
You can use this method which have completionHandler to get back the result from your web service
func postAsync(backendMethodName:String ,body: [[String:String]], completionHandler: (resultnig:String) -> Void)
{
let session: NSURLSession = NSURLSession.sharedSession()
let urlPath: String = "\(sessionclass.connectionString)/\(backendMethodName)"
let request = NSMutableURLRequest(URL: NSURL(string:urlPath)!)
request.HTTPMethod = "POST"
request.timeoutInterval=10
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(body, options: [])
let task = session.dataTaskWithRequest(request) { data, response, error in
guard data != nil else
{
print("no data found: \(error)")
return
}
let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
completionHandler(resultnig: strData as! String)
}
task.resume()
}
its take the backend method Name (in web service) and your jsonData
you can call it like this :
self.postAsync("checkConnection", body: self.alldictionariesConn, completionHandler: { (resultnig) in
print(resulting) })//resulting = result from your web service