Send post request in XML format using RestKit - objective-c

Hi I am using restkit for first time, and there are several questions that come to my mind. First when sending a post request using restkit what format is the request Json or XML and how can I specify it? I am sending a post request to the server to authenticate a user and should receive a conformation if details correct in XML format.
NSArray *objects = [NSArray arrayWithObjects: email, password, nil];
NSArray *keys = [NSArray arrayWithObjects:#"username", #"password", nil];
NSDictionary *params = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
[[RKClient sharedClient] post:#"/login" params:params delegate:self];
This is the code I am using, the xml accepted by the web services should look like
<login>
<username>user#example.com</username>
<password>Password</password>
</login>
It is sending the request,but I am not getting the right response. Is there a way to view what is the format of the request I am sending to the server ?

Sounds like you are using the wrong call. The post call you are using assumes that the rest service wants params like login=username&password=skdjgh, i.e. NOT in XML, but in 'normal REST format'. You need to either find a call to post a block of text using RestKit, or use another call. In other words you need to create the XML yourself (or use some library) then send that via a post.

Give this a try. I think it is supposed to do what you want. I never got it to work but I had other things wrong with my code.
RKObjectManager* objectManager = [RKObjectManager objectManagerWithBaseURL:#"http://mysite.com"];
objectManager.serializationMIMEType = RKMIMETypeXML;
Note: this may be what you do for sending XML back to you, not sending XML to the server. Don't know off the top of my head.

You can use RKRequestSerialization class to do the xml serialization for you.
Here is a code snippet from one of my projects:
#import <RestKit/RKRequestSerialization.h>
...
[RKObjectManager sharedManager].acceptMIMEType = RKMIMETypeTextXML;
NSString *loginData = #"<Login><UserId>test</UserId><Password>test</Password></Login>";
[[RKClient sharedClient] setValue:#"XYZ01" forHTTPHeaderField:#"ServiceId"];
[[RKClient sharedClient] post:#"/login"
params:[RKRequestSerialization serializationWithData:[loginDetails dataUsingEncoding:NSUTF8StringEncoding] MIMEType:RKMIMETypeTextXML]
delegate:self];
And, here is the dump using RKLogConfigureByName("RestKit/Network", RKLogLevelTrace)
2012-08-12 11:28:41.476 MyApp[730:707] T restkit.network:RKRequest.m:402 Prepared POST URLRequest '<NSMutableURLRequest https://someserver.com/MyApp/rest/service/request>'. HTTP Headers: {
Accept = "text/xml";
"Content-Length" = 75;
"Content-Type" = "text/xml";
ServiceId = XYZ01;
}. HTTP Body: <Login><UserId>test</UserId><Password>test</Password></Login>

I did objectManager.serializationMIMEType = RKMIMETypeXML; but the Content-Length of my request is 0, and the HTTP Body is empty. If I use RKMIMETypeJSON the request looks fine, but the xml serialization doesn't work. Any ideas why this happens? What else do I need to do to post xml with valid serialization?
I also found this in doc: "RestKit currently supports serialization to RKMIMETypeFormURLEncoded and RKMIMETypeJSON". So, how can I use RKObjectManager to post xml?

I solved this using ASIHTTPRequest and XMLWriter. Unfortunate, but RestKit just doesn't seem to support XML posts out the box.
I do use RK for GETting stuff though.

Related

retrieving certain keys from a returned JSON in Objective C

I am sending a JSON encoded POST type to a server of mine which reads the sent information in PHP and decodes it there. Now when I re-encode it and send it back it works perfectly and I can NSLog the response but my issue is how do I get a specific section of the response?
Here is an example response:
responseString: {"status":"ok","code":0,"original request":{"username":"test"
`,"password":"test"}}`
Suggestions, thoughts?
What you are receiving is actually a 'dictionary of objects'. You can separate the data in the above code as follows:
First, serialize the response data using JSON serialization as follows:
NSError* error;
NSDictionary* responseDictionary = [NSJSONSerialization JSONObjectWithData:returnData options:nil error:&error];
You may then separate the dictionary as you wish.For instance, if you want to retrieve the value for "status", you may use:
NSString *status = [responseDictionary objectForKey:#"status"];
Or, if you want to retrieve "original request" which is another dictionary, you may use:
NSDictionary *originalRequest = [responseDictionary objectForKey:#"original request"];
Hope this helps!
The response received from the server is JSON data.
Here is an excellent tutorial on JSON parsing for iOS and there are plenty of tutorials & docs if you browse.
http://www.raywenderlich.com/5492/working-with-json-in-ios-5
In JSON, "{}" represents a dictionary and "[]" is an array. So, try this
NSDictionary* originalRequest = [responseString objectForKey:#"original request"];
you can dig in further like this,
NSString* username = [originalRequest objectForKey:#"username"];
I strongly recommend you to read some tutorials on JSON.

Why NSURLErrorDomain -1000 (bad URL) for specific NSURLs only?

I looked through all the relevant questions I could find (search: "NSURLErrorDomain -1000"), and couldn't find a solution. Maybe somebody eagle-eyed could help me.
I'm making calls from iOS 6 to the Instagram API (but the parts that work, did so already in iOS 5.1). I'm using Koolistov's NSURL+PathParameters.h as a helper, and my understanding is that it does indeed escape everything that is necessary in the URL.
Some preliminaries:
NSDictionary *parameters = nil;
NSURL *URL = [NSURL URLWithString:#"https://api.instagram.com/"];
parameters = [NSDictionary dictionaryWithObjectsAndKeys:
INSTAGRAM_CLIENT_ID, #"client_id", nil];
URL = [URL URLByReplacingPathWithPath:#"/v1/tags/awesome/media/recent"];
URL = [URL URLByAppendingParameters:parameters];
(INSTAGRAM_CLIENT_ID is a macro that contains my app's client ID.)
This URL works as expected.
However, the URL constructed as follows gives me NSURLErrorDomain code = -1000 consistently (on device and on both iPhone 5.1 and 6.0 simulators):
double latitude = 48.858844;
double longitude = 2.294351;
parameters = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithDouble:latitude], #"lat",
[NSNumber numberWithDouble:longitude], #"lng",
[NSNumber numberWithInt:2000], #"distance",
INSTAGRAM_CLIENT_ID, #"client_id",
nil];
URL = [URL URLByReplacingPathWithPath:#"/v1/media/search"];
URL = [URL URLByAppendingParameters:parameters];
I then create an NSURLRequest:
NSURLRequest *req = [NSURLRequest requestWithURL:URL];
If I print out the URL from the NSURLRequest, it seems quite OK:
https://api.instagram.com/v1/media/search?client_id=REDACTED&distance=2000&lat=48.858844&lng=2.294351
I'm using MyDownloader from Matt Neuburg's Programming iOS 5, Chapter 37. I create an instance of MyDownloaderand pass it the NSURLRequest:
MyDownloader *downloader = [[MyDownloader alloc] initWithRequest:req];
Accoding to RFC 3986, underscores or dots don't need to be escaped, I don't see anything else to be escaped, and NSURL+PathParameters.h should handle escaping anyway. So what is going on here? What am I missing?
To add to the weirdness, also this even simpler way of constructing the URL gives the same error:
parameters = [NSDictionary dictionaryWithObjectsAndKeys:
INSTAGRAM_CLIENT_ID, #"client_id", nil];
URL = [URL URLByReplacingPathWithPath:#"/v1/media/popular"];
It also looks OK:
https://api.instagram.com/v1/media/popular?client_id=REDACTED
All of these URLs work in the Instagram API console and directly in Safari. These API calls don't need OAuth authentication, just the client ID.
I would be greatful if somebody could point out what the problem is.
EDIT: encoded the lat and lng values with:
[[[NSNumber numberWithDouble:latitude] stringValue]
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
No change in URL or behavior. But when I replace the dots with literal %2E's, the path helper encodes the percent sign, resulting in %252E...
Solved. There was nothing wrong with the URLs shown above. The problem was with surrounding code. I'm answering my own question so that someone working with the Instagram API might benefit from the answer.
It turns out that the /v1/tags/tagname/media/recent endpoint of the API returns a pagination object in the JSON response, but the /v1/media/search and /v1/media/popular do not. From inside that object you can find the URL to the next batch of photos, in a value called next_url.
Because I hadn't read the documentation properly (and it was a little confusing in places), and because the different Instagram API endpoints work differently, my code expected to find the next_url value in all those cases. So the "bad URL" I was getting from NSURLConnection was referring to the second connection, and not the initial URL. Since I did not realize there is no pagination in the two other cases, I assumed that the problem must be in the initial URL.

RestKit PUT not working

I'm trying to do a fairly basic HTTP PUT using RestKit. I don't want to put the entire object, since the API call was designed to accept a single query parameter and just update that field. I've tried two approaches so far, both unsuccessful.
URL to post to: https://myserver/api/users/{userId}
Query string parameter: verificationCode=
Example usage: PUT https://myserver/api/users/101?verificationCode=646133
Approach #1: Put the query parameter in a RKParams object and make the PUT call with those params.
NSString *putUrl = [NSString stringWithFormat:#"/api/users/%i", [APIUserInfo sharedAPIUserInfo].apiUserIdx];
NSLog(#"the PUT url is %#", putUrl);
// Send a PUT to a remote resource. The dictionary will be transparently
// converted into a URL encoded representation and sent along as the request body
NSDictionary* paramsDict = [NSDictionary dictionaryWithObject:[_verificationCode text] forKey:#"verificationCode"];
// Convert the NS Dictionary into Params
RKParams *params = [RKParams paramsWithDictionary:paramsDict];
[[RKClient sharedClient] put:putUrl params:params delegate:self];
Approach #2: Build the entire url and try a PUT with params set to nil.
NSString *putUrl = [NSString stringWithFormat:#"/api/users/%i?verificationCode=%#", [APIUserInfo sharedAPIUserInfo].apiUserIdx, [_verificationCode text]];
NSLog(#"the PUT url is %#", putUrl);
[[RKClient sharedClient] put:putUrl params:nil delegate:self];
Neither approach is working for me. The first fails saying "RestKit was asked to retransmit a new body stream for a request. Possible connection error or authentication challenge?" then runs for about 10 seconds and times out. The second approach fails saying HTTP Status 405 - Method Not Allowed.
Can anyone point out where I'm going wrong, or provide me with a simple PUT example using RestKit? Most of the examples I've found at there are putting the entire object which I don't want to do in this case.
UPDATE:
Approach #2 worked well once I got a few things sorted out on the server side. Final solution:
NSString *putUrl = [NSString stringWithFormat:#"/api/users/verify/%i?verificationCode=%#", [APIUserInfo sharedAPIUserInfo].apiUserIdx, [_verificationCode text]];
NSLog(#"the PUT url is %#", putUrl);
[[RKClient sharedClient] put:putUrl params:nil delegate:self];
the HTTP PUT method is disabled on your webserver. It is by default on all webserver for security reasons.
HTTP Status 405 - Method Not Allowed.

iOS: RestKit loadObject & send params

using loadObjectAtResourcePath on GET method, doesn't include my parameters on the requests.
for example, if I send:
[RKObjectManager objectManagerWithBaseURL:#"http://something/ws"];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"/res" delegate:self block:^(RKObjectLoader *loader) {
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
#"val", #"param1",
nil];
loader.params = [RKParams paramsWithDictionary:dict];
}];
the final url request doesn't include the "?param1=val" part - why?
Update Months Later
The real answer is that loader.params creates the HTTP BODY, hence it works for POST, PUT, DELETE etc but not for GET where the params are appended to the URL.
Hence, the answer below still works if you're facing the same issue for GET, but if you're sending out GET requests, it's mostly using methods that attach the params to the query string.
To summarize the differences between the two.
Sending params in the HTTP Body(i.e. POST, UPDATE, DELETE)
// Convert a NS Dictionary into Params
RKParams *params = [RKParams paramsWithDictionary:optionValues];
// I use sendObject to skip the router. Otherwise it's normally postObject
[[RKObjectManager sharedManager] sendObject:yourObject toResourcePath: yourResourcePath usingBlock:^(RKObjectLoader *loader) {
loader.method = RKRequestMethodPOST;
loader.delegate = delegate;
loader.params = params; // This sets params in the POST body and discards your yourObject mapping
} ];
Caveat Emptor (for above)
Setting params in the block destroys any mapping that you might have set in yourObject, kind of defeats the purpose of using object mapping. There's a fix here by Sebastian loader.params - Extra params if you really want to use this method to append extra parameters to your Post not in the object.
Sending in params as Query String (i.e. GET)
// Make a NS dictionary and use stringByAppendingQueryParameters
NSDictionary *shopParams = [NSDictionary dictionaryWithKeysAndObjects:
#"limit",#"20",
#"location",#"latitude,longitude",
nil];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[#"/api/v1/shops.json" stringByAppendingQueryParameters:shopParams] delegate:objectDelegate];
The rest of the answer is just for reference, I'm a hoarder.
Old Answer
I'm using RestKit for my project and facing the same issue.
I think RKParams is mainly used to do POST requests. I cannot fully decipher your code because 1) I don't know loader's declaration? 2) RKParams is not to be used with Object Manager?
I did this.
Loader Method in App Delegate
NSDictionary *shopParams = [NSDictionary dictionaryWithKeysAndObjects:#"limit",#"30", nil];
[[RKClient sharedClient] get:#"/api/v1/shops.json" queryParams:shopParams delegate:self];
Delegate
- (void)requestDidStartLoad:(RKRequest *)request {
NSLog(#"RK Request description: %#",[request description]);
}
Output:
RK Request description: <RKRequest: 0x7993db0> and rails log say {"limit"=>"30"}.
From the autocomplete in Xcode, you can see the get request didn't even use RKParams. Just a NSDict. The POST requests uses it.
My goal is to attach a query string, i.e. ?location=singapore&etcetc to my API methods in Rails. For this, RK comes with a NSString addon called appendQueryParams RK docs link that you can use to append query params.
If your goal is POST images etc, you can follow the above line of thought of using RKClient.
Update:
If you just want to append parameters to Object Manager
NSDictionary *shopParams = [NSDictionary dictionaryWithKeysAndObjects:
#"limit",#"20",
#"location",#"latitude,longitude",
nil];
This is outdated and marked for deprecation.
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[#"/api/v1/shops.json" appendQueryParams:shopParams] delegate:self];
Use this instead:
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[#"/api/v1/shops.json stringByAppendingQueryParameters:shopParams] delegate:yourLoaderDelegate];
Rails Log: {"location"=>"latitude,longitude", "limit"=>"20"}
Hope in my answer I didn't make any wrong statements.
Refer to this question RestKit GET query parameters.

ASIHttpRequest's post body is null sometimes when building through NSOperation

I have a setup where I'm using ASIHttpRequest to make service calls in a background NSOperation subclass. I call a method from my main() in the NSOperation and have that method go through and build out my request with appropriate URL, headers, body, etc. The problem is that I'm occasionally seeing the request body set to null instead of the expected value. I print out what the expected body should be right before setting it to the request and that representation is NOT null, so I don't know where/how the request body for the ASIHttpRequest gets released.
Here's some sample code of how I'm setting the request body ... this method gets called by another driver method that controls the whole networking workflow. That method, in turn, is called by the main() method of my NSOperation.
+ (ASIHTTPRequest*) buildRequestForProjectModify: (ANVideoProject*) theProject {
if (theProject.selfUrl == nil) return nil; //Can't do anything if we don't have a project url.
NSString* projectPutUrl = theProject.selfUrl;
ASIFormDataRequest* request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:projectPutUrl]];
[ANAppsServiceHelper addStandardHeadersToRequest:request];
NSDictionary* projectDict = [theProject jsonFriendlyForSaveAndPreview];
ANLogInfo(#"\n\nProject Post body: \n%#\n\n", projectDict); //This will print out ok
ANLogInfo(#"\n\nProject Post Body as JSON\n%#\n\n", [projectDict JSONRepresentation] ); //this too prints out ok.
NSString* jsonRep = [projectDict JSONRepresentation];
NSData* pd = [jsonRep dataUsingEncoding:NSUTF8StringEncoding];
[request appendPostData:pd];
[request setRequestMethod:#"PUT"];
return request; //I have a problem in the calling method from here where the request body is now null.
}
I read around about setting up an autorelease pool in the main method of my NSOperation subclass, and so I am doing that at the top of the main method. When I did that, the occurrence of (null) for the request body was mitigated, but still happened roughly 1/4 of the time I invoked this operation. Strange thing is that no other part of the request ever seems to get set to null in such a random fashion (ie request method or headers). Here is log output from the driver method I mention above printing out the request that was returned:
Here is Modify the request: https://<service-url>/projects/p0FvVjc790MFWduhhqUStA
Here is the request headers: {
Accept = "application/json";
"Accept-Encoding" = gzip;
Authorization = "Bearer <key>";
"Content-Length" = 293;
"Content-Type" = "application/json";
"User-Agent" = "iPhone (OS 4.3.2)";
}
Here is the request method: PUT
Here is the request body: (null)
Update:
So one thing I'm noticing is that sometimes the request body is null and also sometimes there is some garbage data added on to the end of the request body (and sometimes the request body is totally fine). Even more strangely, the request actually always goes through successfully with the body that I intended to add (even if the printout looks bad). This suggests that things are ok in the ASIHttpRequest but perhaps there is something going on with how I am printing out the state of my request or some other issue between ASIHttpRequest and the SBJSon library I'm using that causes the request to look corrupted from time to time (even though it apparently is not).
Did you print out the content of pd (NSData *)? Is the size very big? Then you may need to stream post data from disk:
[request setRequestMethod:#"PUT"];
[request setPostBodyFilePath:#"/Users/ben/Desktop/another-big-one.txt"];
[request setShouldStreamPostDataFromDisk:YES];