I've tried lots of different things to recreate this curl request, and I'm going insane. If someone could help, I'd be very much appreciative.
curl -H "Content-Type: application/json" -X POST --data '[{"a": 4, "b": 5, "c": 6}]' \ -u username:password \ https://www.mywebsite.com
My latest attempt is:
let loginString = NSString(format: "%#:%#", username!, password!)
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = loginData.base64EncodedStringWithOptions(nil)
var url: NSURL = NSURL(string: "www.mywebsite.com")!
var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
request.addValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
let bodyData = JSON(data!).rawString(encoding: NSUTF8StringEncoding, options: NSJSONWritingOptions.allZeros)
request.HTTPBody = bodyData?.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, data, error) in
if let tabBarVC : TabBarController = self.tabBarController as? TabBarController {
println(NSString(data: data, encoding: NSUTF8StringEncoding))
}
}
In this example, data is a dictionary ([String:String]) and JSON is form SwiftyJSON.
The above sends NSData to the server, which is encoded and the server doesn't like. The request is getting through, but the server can't read the data right. What do I need to do to exactly replicate the curl request above? I'm stuck!
Updated to reflect good responses below.
I did attempt to use application/json for content-type, but this returns the message:
{"status": "Internal error JSON uploads must be formatted as an array of objects", "code": 400, "version": "v1"}
It seems like progress if I use text/plain (as above) I get:
{"status": "Column a is missing", "code": 400, "version": "v1"}
Which I can't seem to figure out.
A couple of thoughts:
If nothing else, the line that says:
request.addValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type")
Should be:
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
Your curl clearly states that the content type header was supposed to be JSON, so I'd set that accordingly.
Also, the line that says:
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Accept-encoding")
That's clearly not correct, either. I think you meant Accept not Accept-encoding, plus the server is undoubtedly not responding with a x-www-form-urlencoded response, anyway.
I would simply remove that line.
Your URL is missing the scheme (the http://), but I assume that was removed when editing your question, because I don't think it would work without that, but you assured us that the server did receive the request.
I'd suggest you consider a tool like Charles. Watch the request from curl and then again from your code. Compare and contrast and identify how they differ. Using this, you should be able to diagnose precisely what's going on and where the discrepancy rests.
You say:
The request is getting through, but the server can't read the data right.
If the problem persists, you should share with us how you know the request is getting through and what you mean by "can't read the data right."
Related
I am trying to work with a MS Translator API from Swift 3 (right now playing in playgrounds, but the target platform is iOS). However, I got stuck when I was trying to get an access token for OAuth2. I have following code (I tried to port the code from example at Obtaining an access token):
let clientId = "id".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let clientSecret = "secret".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let scope = "http://api.microsofttranslator.com".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let translatorAccessURI = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
let requestDetails = "grant_type=client_credentials&client_id=\(clientId)&client_secret=\(clientSecret)&scope=\(scope)"
let postData = requestDetails.data(using: .ascii)!
let postLength = postData.count
var request = URLRequest(url: URL(string: translatorAccessURI)!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("\(postLength)", forHTTPHeaderField: "Content-Length")
request.httpBody = postData
URLSession.shared.dataTask(with: webRequest) { (returnedData, response, error) in
let data = String(data: returnedData!, encoding: .ascii)
print(data)
print("**************")
print(response)
print("**************")
print(error)
}.resume()
Of course, I used a valid clientId and a valid clientSecret.
Now the callback prints following information. First, the returnedData contain a message that the request was invalid, along with a following message:
"ACS90004: The request is not properly formatted."
Second, the response comes with a 400 code (which fits the fact that the request is not properly formatted).
Third, the error is nil.
Now I was testing the call using Postman, and when I used the same URI, and put the requestDetails string as a raw body message (I added the Content-Type header manually), I got the same response. However, when I changed the body type in Postman UI to application/x-www-form-urlencoded and typed in the request details as key value pairs through its UI, the call succeeded. Now it seems that I am doing something wrong with the message formatting, or maybe even something bad with the Swift URLRequest/URLSession API, however, I cannot get a hold on to what. Can somebody help me out, please? Thanks.
OK, so after some more desperate googling and experimenting I have found my error. For the future generations:
The problem resided in encoding the parameters in the body of the PUT http request. Instead of:
let scope = "http://api.microsofttranslator.com"
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
I have to use the following:
let scope = "http://api.microsofttranslator.com"
.addingPercentEncoding(withAllowedCharacters:
CharacterSet(charactersIn: ";/?:#&=$+{}<>,").inverted)!
Seems that the API (or the HTTP protocol, I am not an expert in this) have problems with / and : characters in the request body. I have to give credit to Studiosus' answer on Polyglot issue report.
I tried to send body in PUT request but the data is not received on backend
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "PUT"
let putBody = "bucket=\(bucket)&day=\(day)"
request.HTTPBody = putBody.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {(data, response, error) -> Void in
dispatch_async(dispatch_get_main_queue(), {() -> Void in
completion(data: data, response: response as! NSHTTPURLResponse, error: error)
})
})
task.resume()
The value of bucket (Int) is 2015040 and day (String) is day27. I tried making same request in Postman, the server received body data so there is nothing wrong with the server.
Is there any other way to set body of a request?
EDIT :
It's working perfectly if I change request method to POST in my request and server as well. So the question comes down to how to set body in PUT request?
I've just been dealing with this. Had the exact same issue where it was working perfectly fine with POST but not with PUT or PATCH requests. It seems that for PUT and PATCH requests, Swift is picky about the Content-Type header.
So try adding this:
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
This will work as long as your body is url style, so like this:
key=value&key=value&key=value
For those of you using JSON, just use application/json instead for the Content-Type.
I have seen two kinds of methods to make up the HTTP body.
First one is:
let url = NSURL(string: "http://example.com")
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
let postString = "id=13&name=Jack"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
Second one is:
let url = NSURL(string: "http://example.com")
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
let params = ["id":"13", "name":"Jack"] as Dictionary<String, String>
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
When I directly print out the request.HTTPBody the data is different. So I am wondering are there any differences between these two methods in terms of the implementation of the server side? Assuming I'am using PHP.
there're two format data.
in code using postString.dataUsingEncoding it will send data in urlencoded format. In client you must set request's Content-Type header to "application/x-www-form-urlencoded" or something like "application/x-www-form-urlencoded charset=utf-8"
in code using NSJSONSerialization.dataWithJSONObject it will send data in json format. In client you must set request's Content-Type header field to "application/json"
I'm iOS dev so I don't know about format's effect to server side PHP. to answer your question you must find difference between application/x-www-form-urlencoded and application/json format in server side
I try to update a object in the database trough a API call. I do this action with a PUT request. A Post request was already made and is working fine. I thought it is just copy paste...
The API part works fine because if I execute the following curl command than it updates the row I wanted in the database
$ curl -H 'Accept: application/json' -i -X PUT -d 'UserLogin=Ub0D&EndDate=2014-01-17 00:00:00.0000000&Sport=Fietsen&Distance=1000&Duration=10&Achieved=true&DateCreated=2015-05-09 12:01:00.000000' http://localhost:8089/api/goal/ub0d -k -u ub0d:test123
But now the part in swift gives me a bad request error (400)...
This is my code
var url : NSURL
url = NSURL(string: _apiUrl + connectToApi(apiCall) + String("\(AccountRepository.Instance.getCurrentUserName())"))!
let request = NSMutableURLRequest(URL: url)
if postString != nil {
let postData : NSData = ("UserLogin=Ub0D&EndDate=2014-01-1700:00:00.0000000&Sport=Fietsen&Distance=1000&Duration=10&Achieved=true&DateCreated=2014-01-1700:00:00.0000000").dataUsingEncoding(NSUTF8StringEncoding)!
// create the request
request.HTTPMethod = "PUT"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
request.setValue("application/json", forHTTPHeaderField: "Content-type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.HTTPBody = postData
}
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: true)!
I'm really stuck on it:(
You request can't have two different content types at the same time:
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
request.setValue("application/json", forHTTPHeaderField: "Content-type")
The data you are sending are obviously not JSON, so I guess you want only
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
and remove the "application/json" line.
There is also a difference between your date formats:
CURL
EndDate=2014-01-17 00:00:00.0000000
Swift
EndDate=2014-01-1700:00:00.0000000
Note the missing space between date and time, probably should be URL encoded (+):
EndDate=2014-01-17+00:00:00.0000000
In general, most REST APIs send error messages in response body, so it is always good to log the response for failed requests.
I'm getting {"error_description":"Missing grant_type parameter value","error":"invalid_request"} when trying to request access token for the first time. My code is below:
let params : [String : String] =
["client_id" : clientID,
"client_secret" : secret,
"redirect_uri" : redirectURL,
"code" : code,
"grantType" : "authorization_code"
]
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
request.addValue("aapplication/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.addValue("\(request.HTTPBody!.length)", forHTTPHeaderField: "Content-Length")
What might be the problem? Thank you in advance.
Wow, I don't want to sound harsh, but there are multiple issues with this code.
You are not sending grant_type, but you do send grantType.
You are encoding the post body as application/json, but you are setting the Content-Type to be aapplication/x-www-form-urlencoded which seems to be a misspelling of application/x-www-form-urlencoded.
So two possible spelling issues and a content type mismatch.
Work out if you need to send application/x-www-form-urlencoded or application/json. Format the data and set the content type as needed.
Double check the spelling of everything.