This is my Json request
var postsEndpoint = "http://test/Search"
let test = ["SearchCriteria":["ForeName":"jack", "Surname":"jill"]];
request(.POST, postsEndpoint, parameters: test, encoding: .JSON)
.responseJSON { (request, response, data, error) in
if let anError = error
{
println("error calling POST on /posts")
println(error)
}
else if let data: AnyObject = data
{
let post = JSON(data)
println("The post is: " + post.description)
}
Is there an issue with my request as I am getting the following error:
error calling POST on /posts
Optional(Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (Invalid value around character 3.) UserInfo=0x7fdd00414c80 {NSDebugDescription=Invalid value around character 3.})
Late, but I found this thread.
I ran into this same problem running a Django dev server and got You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data.
Append a slash to postsEndpoint so it becomes...
var postsEndpoint = "http://test/Search"
Related
I am trying to show details of an api call in my electron app. this is the api and sample json data:
[
{
ID: 596,
title: "Carolyn Wood",
content: "<p>Turning away bad clients can leave you feeling oddly guilty. They’re sort of like alcoholic or meth head cousins who force you, by their own bad behavior into denying them things you wouldn’t deny other people. You’re left feeling not quite yourself. </p> ",
link: "https://quotesondesign.com/carolyn-wood/",
custom_meta: {
Source: "Twitter"
}
}
]
I am using request module to fetch data and JSON.parse(body) to get the object like below:
let request = require('request');
request("http://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1&_jsonp=mycallback",
(err, response, body)=>{
let jsonBody = JSON.parse(body);
let randQuote = jsonBody[0]["content"];
document.getElementById("quote").innerHTML = randQuote;
});
In console, I get this error:
Uncaught ReferenceError: jsonBody is not defined
at Request.request [as _callback] (D:\ElectronDev\ElectronBasics\quote-widget\index.js:8
pointing on this line JSON.parse(body). How do I resolve this?
The response from the endpoint has some extra characters at the start of the response and that's the reason the JSON.parse can not parse it into object and jsonBody is undefined.
Here is the fix to your specific response for this. Change your line of code to this.
let jsonBody = JSON.parse(body.substring("/**/mycallback(".length, body.length - 1));
Hope this helps.
Here is my method for hitting their API for their Authorization Code Flow:
class func obtainAuthTokenPackage(authCode: String) throws
{
//Create a request
var request = URLRequest(url: Gimme.theSpotify.urlFor(endpoint: .requestingTokens)) //"https://accounts.spotify.com/api/token"
request.httpMethod = "POST"
//Build the header
let spotifyClientCreds = Gimme.theSpotify.clientID + ":" + Gimme.theSpotify.clientSecret
let encodedCreds = spotifyClientCreds.data(using: .utf8)!.base64EncodedString()
request.setValue("Basic \(encodedCreds)", forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
//Build the body
var dict = [String:String]()
dict["grant_type"] = "authorization_code"
dict["code"] = authCode
dict["redirect_uri"] = Gimme.theSpotify.redirectURI
var package = Data()
do
{
package = try JSONSerialization.data(withJSONObject: dict)
}
catch
{print("oopsie")}
request.httpBody = package
//Set up a web transaction
let transaction = URLSession.shared.dataTask(with: request) {
(possData, possResp, possErr) in
if let data = possData
{
print(String(data: data, encoding: .utf8)!)
}
}
//Do it
transaction.resume()
}
The print statement near the bottom produces {"error":"server_error","error_description":"Unexpected status: 415"}
Things I've Tried Already:
Changing request.setValue(... to request.addValue(... and vice versa doesn't seem to make a difference.
Using application/x-www-form-urlencoded, changing the http body to "grant_type=authorization_code&code=" + authCode + ...".data(using: .utf8).
When I did this, API responds with a message that I need to set the grant type to authorization_code (which tells me their server isn't parsing my http body correctly).
Moving client credentials from the header to the body (using JSON).
Creating my http body using Swift 4's new JSON encoding tools
Requesting using the Rested app (it's like Postman or HTTPRequestor)
Implementing their Implicit Grant Flow with success. But that doesn't give a refresh token and I need it :(.
Sobbing internally so I don't distract people around me
Changing Content-Type to content-type
Removing the header field that specifies content type
Escaping characters in the redirect-uri (e.g. replacing colons with %3A, slashes with %2F)
Questions I Have:
Status 415 means unsupported media type, so is the Spotify API expecting application/x-www-form-urlencoded instead of JSON?
If you can get Spotify's Authorization Code Flow working in your Swift project, what did you do different?
If you use application/x-www-form-urlencoded, how did you create your http body?
I appreciate any help, thanx guys.
I DID IT! HAHAHAHAHAHAHAHAHAHA!!!!!!!!!!!
//The endpoint expects form-urlencoded
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
//Build a string that has client_id, client_secret, grant_type, the code, and the redirect uri
//It has to be in that order (I didn't try any other order, but I've been having this problem for so long, I don't want to risk anything)
//It has to be in the form "key=value&key=value..." (Standard form url encoded format)
var formEncoded = "client_id=\(Gimme.theSpotify.clientID)"
formEncoded.append("&client_secret=\(Gimme.theSpotify.clientSecret)")
formEncoded.append("&grant_type=authorization_code")
formEncoded.append("&code=\(authCode)")
formEncoded.append("&redirect_uri=\(Gimme.theSpotify.redirectURI)")
//Finally convert it into UTF8 data
let bodyData = formEncoded.data(using: .utf8) //Check that this isn't nil, then proceed
//Stick this stuff in the transaction, and it'll be SUCCESSFULLLLL
Questions I had that I am able to answer now:
- This api endpoint https://accounts.spotify.com/api/token application/x-www-form-urlencoded as the Content-Type.
- What I did different: I included client ID and client secret before the grant_type key in the body instead of putting it in the header
- I created my http body manually as demonstrated by the code segment above.
In conclusion:
- Nothing really fancy needed
- Spotify API documentation is lacking (but c'mon, who's isn't?)
- I'm relieved
I am trying to send a very simple dictionary to the json file in my local host using swift (Alamofire)
Here is what I've done :
let parameters: Parameters = ["name" : "Danial"]
Alamofire.request("http://localhost/testing.json", method: HTTPMethod.post, parameters: parameters).response { result in
if result.response?.statusCode != nil {
if let status = (result.response?.statusCode)! as? Int {
print("status : \(status)")
}
}
}
and inside my testing.json I have the following :
{
"x":"y"
}
and I get the the http status 412 (frequently) and the 200 (without in apperance of the new json in the json file) rarely . I am very new to this networking stuff . thus , please dont attack my question as if I must know this simple thing . This has already taken me 2 days to resolve yet i am here :|
by the way there should be no error in connection as my get protocol seems to be working fine
OK, here are a couple of things I see.
When you send a POST to a server, the URL must be to a Web Service or a web app of some kind. Here it appears you are trying to POST to a resource file. Static resource files will not update automatically.
You didn't specify the encoding, so you didn't post JSON (application/json), you posted Form URL Encoded (application/x-www-form-urlencoded). Instead of being { "name": "Danial" }, you sent name=Danial.
You need to set the encoding to JSON.
let parameters: Parameters = ["name" : "Danial"]
Alamofire.request("http://localhost/testing.json",
method: HTTPMethod.post,
parameters: parameters,
encoding: JSONEncoding.default).response { result in
if result.response?.statusCode != nil {
if let status = (result.response?.statusCode)! as? Int {
print("status : \(status)")
}
}
}
Important Fact
I forgot to mention an important factor in the question. I am running this in a TestCase. I think this issue has something to do with the TestCase not awaiting for async completionHandler to return
Migrated out from Alamofire to SwiftHTTP, since I found this much easier.
On SwiftHHTP there is no way to know what URL got generated, what error it returned. For example, I tried to see the opt.debugDescription variable, it returned something cryptic like description String "<SwiftHTTP.HTTP: 0x60000007e540>"
Steps I have followed
I have set YES to Allow Arbitrary Loads.
Safari on the iPhone Simulator responds with the correct JSON if I paste fullurl ->http://120.0.0.1:8080/myapi/Driver/getDriver?driver=2243&domain=4345&key=asdfasdf. Even catalina.out on the tomcat server running on my mac responds with a debug message.
But when I run this in a test case under Xcode the below code prints none of debug print's.
--1->, --2-->, --3-->, nothing got printed.
Debugger breakpoints also dont stop here.
CODE
var getData = [String:String]()
getData = ["domain": "4345",
"driver" : "2343",
"key" : "asdfasdf"]
var urlComponents = URLComponents(string: fullURL)!
var queryItems = [URLQueryItem]()
queryItems = self.getData.map{ URLQueryItem(name : $0.0, value : $0.1) }
urlComponents.queryItems = queryItems
print("fullurl ->"+(urlComponents.url)!.absoluteString)
do {
let opt = try HTTP.GET((urlComponents.url)!.absoluteString)
opt.start { response in
if let err = response.error {
print("--1-> error: \(err.localizedDescription)")
return //also notify app of failure as needed
}
print("--2--> opt finished: \(response.description)")
self.responseData = response
}
} catch let error {
print("--3--> got an error creating the request: \(error)")
}
EDIT
Even after changing the code to https or http://www.google.com, same result.
let testComponents = URLComponents(string: "https://www.google.com")!
URLSession.shared.dataTask(with: (testComponents.url)!, completionHandler: {
(data, response, error) in
if(error != nil){
print("..1>..")
}else{
do{
print ("..2>.." )
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String : AnyObject]
self.responseData = json
}catch let error as NSError{
print("..3>..")
}
}
}).resume()
EDIT 1
Tried from here #Vivek's answer.
callWebService(url: (urlComponents.url)!.absoluteString)
.
.
func callWebService(url : String) {
.
.
let callURL = URL.init(string: url)
Nothing got printed again, Error / JSON, nothing.
Yes, Unit Tests don't wait by default for the completionHandler to be called. If you call asynchronous functions in tests, you don't need to change the function's code, but the behavior of the test.
The solution: XCTestExpectation
In your test-class (the subclass of XCTest), add this property:
var expectation: XCTestExpectation?
A test-function for an asynchronous request could basically look like this:
func testRequest() {
expectation = expectation(description: "Fetched sites") //1
//2
some.asyncStuffWithCompletionHandler() {
someData in
if someData == nil {
XCTestFail("no data") //3
return
}
//looks like the request was successful
expectation?.fulfill() //4
}
//5
waitForExpectations(timeout: 30, handler: nil)
}
Explanation
This defines, what you expect the tested code to do. But actually, it's not important, what you add as description. It's just an information for you, when running the test
This is the function with a completionHandler, you are calling
If you want to let the test fail within the completionHanlder, call XCTestFail()
If everything in the completionHandler worked as expected, fulfill the expectation by calling expectation?.fulfill.
Here comes the important part: This part of the code will be executed before the completionHandler! If this would be the end of the function, the test would be stopped. That's why we tell the test to wait until the expectations are fulfilled (or a certain amount of time passed)
There is an interesting blog post about Unit Tests. (see the section "XCTestExpectation") It's written in an old Swift syntax, but the concept is the same.
I am retrieving data from URL like this:
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error == nil {
let swiftyJSON = JSON(data: data!)
let results = swiftyJSON[0]["name"]
print(results)
} else {
print("error")
}
}
For the above, I get data like this:
[
{
"_id":"123",
"_rev":"345",
"name":"hey"
},
{
"_id":"133",
"_rev":"33345",
"name":"hello"
}
]
I always end up in error block and I am not sure why?
I pasted the JSON in chrome console and able to do swiftyJSON[0].name. I would like to print all elements from the above json OBJECT.
Error:
error Optional(Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo={NSUnderlyingError=0x7f87514ab570 {Error Domain=kCFErrorDomainCFNetwork Code=-1003 "(null)" UserInfo={_kCFStreamErrorCodeKey=8, _kCFStreamErrorDomainKey=12}}, NSErrorFailingURLStringKey=http://localhost:3000/idea, NSErrorFailingURLKey=http://localhost:3000/idea, _kCFStreamErrorDomainKey=12, _kCFStreamErrorCodeKey=8, NSLocalizedDescription=A server with the specified hostname could not be found.})
Please note, localhost:3000 is on.
The error you pasted may be the request's hostname not found.
"A server with the specified hostname could not be found." The JSON parse seems right totally.
The error is not in the JSON data. The data cannot be retrieved in the first place since the URL http://localhost:3000/idea is not working.
Most likey, the URL is valid on your Mac but not on your iPhone. The URL would only be valid if your server side was running on the iPhone or simulator itself, which is rather unlikely.
localhost isn't a global address. On your Mac, it refers to your Mac. On an iPhone, it refers to the iPhone itself.
Open the Network Utility app on your Mac, look up the IP address and replace localhost with your IP address, e.g. http://192.168.1.37:3000/idea. Then your iOS app will be able to retrieve the data.