NSURLConnection different result when get error - objective-c

I'm using -[NSURLConnection initWithRequest...] to connect to a server,
and when the server side fails it will respond with an error 400 with a message in the body.
When there is no error, I can get response data in didReceiveData. But when the server returns an error 400, I do not receive any error message in didReceiveData.
However if I use -[NSURLConnection sendSynchronousRequest...] to do the same request
I do get error data from sendSynchronousRequest's return value.
I want to get the error message from didReceiveData, but I dont know why I cannot get it.
What is different between the do, can someone help me?
This way I can get the error message:
NSURL *url = [NSURL URLWithString:#"http://devapi.xxx.com/v1/debug/error"];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:240] autorelease];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
NSLog(#"STATUS:%d", [((NSHTTPURLResponse *)response) statusCode]);
NSLog(#"ERROR:%#", error);
This way I cannot get it:
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://devapi.xxx.com/v1/debug/error"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:240] autorelease];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
urlConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"statusCode:%d",((NSHTTPURLResponse *)response).statusCode);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
NSLog(#"DATA:%#",[NSString stringWithCString:[receivedData bytes] encoding:NSASCIIStringEncoding]);
}

Use this delegate method for handling such errors.
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#" Failed with error---%#",error);
}

I think I found what happen it is,
When I get a error status code in didReceiveResponse,
I immediately try to get data and cancel connection,
But it is wrong,
Because didReceiveData still in receive after didReceiveResponse.
Thanks a lot!

Related

Cocoa: POST headers/parameters lost when accessing protected resources in a Django site using NSURLConnection

I am trying to access protected resources on a Django site using NSURLConnection , OAuth2 Bearer token and HTTPS. I receive a token, which I then attach either to a GET parameter, POST parameter or header. I can access those URL:s which respond to GET parameter. But when I try to access urls using POST, the server returns me a 403 with a custom error message saying there is no header/post parameter containing the token. I have tried several solutions and HTTP libraries. This method uses AFNetworking, I tried it. We even changed the authorization to accept an alternative header due to warnings that apple does not like the modifying of "Authorization" header. My current code looks like this: (scheme == #"https")
-(void) logOut {
NSString *pget = #"/api/logout/";
NSString *path = [pget stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *absolutePath = [NSString stringWithFormat:#"%#://%#%#", scheme, host, path];
NSURL *url = [NSURL URLWithString:absolutePath];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30.0];
NSString *authValue = [NSString stringWithFormat:#"Bearer %#", accessToken];
[urlRequest setValue:authValue forHTTPHeaderField:#"Authorization_Extra"];
[urlRequest setValue:#"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:#"content-type"];
[urlRequest setHTTPMethod: #"POST"];
/*
NSString *post = [NSString stringWithFormat:#"access_token_extra=%#", accessToken];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
[urlRequest setValue:postLength forHTTPHeaderField:#"Content-Length"];
[urlRequest setHTTPBody:postData];
*/
NSDictionary* headers = [urlRequest allHTTPHeaderFields];
NSLog(#"headers: %#",headers);
_originalRequest = urlRequest;
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self startImmediately:NO];
[connection start];
}
#pragma mark NSURLConnection Delegate Methods
- (NSURLRequest *)connection: (NSURLConnection *)connection
willSendRequest: (NSURLRequest *)request
redirectResponse: (NSURLResponse *)redirectResponse;
{
if (redirectResponse) {
// we don't use the new request built for us, except for the URL
NSURL *newURL = [request URL];
NSMutableURLRequest *newRequest = [_originalRequest mutableCopy];
[newRequest setURL: newURL];
NSLog(#"New Request headers: %#", [newRequest allHTTPHeaderFields]);
return newRequest;
} else {
return request;
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
NSLog(#"Received response statuscode: %ld", (long)[response statusCode]);
responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
return nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connection finished:");
[_delegate handleData:responseData];
}
The _Delegate handleData parses the response, and the custom response is always that I am lacking either the header or post parameter needed for the Bearer token.
It seems that even though I am replacing the request with a mutable copy of the original on every redirect, the headers/parameters still get stripped by NSURLConnection. But why, and how, since I'm sending a copy of the original request every time and I verify by logging that they are there?

How do I ignore an "invalid" SSL certificate in Objective-C?

Currently I have:
NSArray* array = [NSArray arrayWithObjects:#"auth.login",#"username",#"password", nil];
NSData* packed_array = [array messagePack];
NSURL* url = [NSURL URLWithString:#"https://192.168.1.149:3790/api/1.0"];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
//Previously the below had changed by hostname.
//[request setValue:#"RPC Server" forHTTPHeaderField:#"Host"];
[request setValue:#"binary/message-pack" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d",[packed_array length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:packed_array];
NSURLResponse *response;
NSError *error;
responseData = [NSMutableData dataWithData:[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]];
NSLog(#"response data: %#",[responseData messagePackParse]);
NSLog(#"error: %#",error);
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
NSLog(#"called canAuthenticateAgainstProtectionSpace");
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSLog(#"called didReceiveAuthenticationChallenge");
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
Which returns "Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid"…"
How should I be implementing the answer from this question and getting data back?
I figured out the problem(s). First I fixed the way I was using the delegate and secondly I changed the host. I edited the code to remove the changing of the host and commented it. I still don't think my question was a duplicate…
If your application flow needs the syncronous request, i answer something similar here Objective-C SSL Synchronous Connection

Request with NSURLRequest

I'm trying do to an app which uses a database (actually in my localhost), I tried with ASIHTTPRequest but having so much troubles with iOS 5 (I learnt how to use ASIHTTPRequest form there : http://www.raywenderlich.com/2965/how-to-write-an-ios-app-that-uses-a-web-service
Now i'm trying with the API provided by Apple : NSURLRequest / NSURLConnection etc,...
I read the Apple online guide and make this first code :
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL
URLWithString:#"http://localhost:8888/testNSURL/index.php"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
[request setValue:#"Hello world !" forKey:#"myVariable"];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
if (theConnection) {
receiveData = [NSMutableData data];
}
}
I added the delegates needed by the API
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- (void)connection:(NSURLConnection *)connection
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
Here is my php code :
<?php
if(isset($_REQUEST["myVariable"])) {
echo $_REQUEST["myVariable"];
}
else echo '$_REQUEST["myVariable"] not found';
?>
So what is wrong? When I start the app, it will immediately crash with this output :
**
**> 2012-04-09 22:52:16.630 NSURLconnextion[819:f803] *** Terminating app
> due to uncaught exception 'NSUnknownKeyException', reason:
> '[<NSURLRequest 0x6b32bd0> setValue:forUndefinedKey:]: this class is
> not key value coding-compliant for the key myVariable.'
> *** First throw call stack: (0x13c8022 0x1559cd6 0x13c7ee1 0x9c0022 0x931f6b 0x931edb 0x2d20 0xd9a1e 0x38401 0x38670 0x38836 0x3f72a
> 0x10596 0x11274 0x20183 0x20c38 0x14634 0x12b2ef5 0x139c195 0x1300ff2
> 0x12ff8da 0x12fed84 0x12fec9b 0x10c65 0x12626 0x29dd 0x2945) terminate
> called throwing an exception**
**
I guess, it means that somethings is wrong with this line :
[request setValue:#"Hello world !" forKey:#"myVariable"];
It actually works if I comment this line.
My question is : How can I send datas to a PHP API, using NSURLRequest and NSURLConnexion?
Thank you for helping.
P.S. By the way, I have poor knowledges about server, PHP ect,...
try this:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL
URLWithString:#"http://localhost:8888/testNSURL/index.php"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
[request setHTTPMethod:#"POST"];
NSString *postString = #"myVariable=Hello world !";
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
if (theConnection) {
receiveData = [NSMutableData data];
}
seen here https://stackoverflow.com/a/6149088/1317080
Try the below code it is one of the simple ways to consume a web service(json)
NSURL *url = [NSURL URLWithString:#"yourURL"];
NSMutableURLRequest *urlReq=[NSMutableURLRequest requestWithURL:url];
NSURLResponse *response;
NSError *error = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:urlReq
returningResponse:&response
error:&error];
if(error!=nil)
{
NSLog(#"web service error:%#",error);
}
else
{
if(receivedData !=nil)
{
NSError *Jerror = nil;
NSDictionary* json =[NSJSONSerialization
JSONObjectWithData:receivedData
options:kNilOptions
error:&Jerror];
if(Jerror!=nil)
{
NSLog(#"json error:%#",Jerror);
}
}
}
Hope this helps.

How do make Json request to server?

I'm doing an application that should work with the server.
Need to be authorized on the server through the program using the username and password.
I need to make a request to the server with the line:
{"login": "mad_fashist", "password": "eqeeuq313371", "method": "authentificator"}
The string must be in Json.
In response, I should get a line if authentication fails:
{"validated": "false", "kuid": "", "sid": "", "uid": ""}
And if authentication is passed:
{"validated":"true","kuid":"6","sid":"834fe9b4626502bf9ff23485a408ac40","uid":"69"}
The question is how to send and receive all of the above?
Use SBJson framework. and do smth like:
-(void)requestProjects
{
//I prepare the string
NSString *preparedString=[NSString stringWithFormat:#"%# %#", self.lastDate, self.currentCategory];
NSDictionary *jsonDict = [NSDictionary dictionaryWithObject:preparedString forKey:#"request"];
//Prepare convert to json string
NSString *jsonRequest = [jsonDict JSONRepresentation];
NSLog(#"jsonRequest is %#", jsonRequest);
//Set the URL YOU WILL PROVIDE
NSURL *url = [NSURL URLWithString:#"http:xxxxxxxxxxxxxxxxxxx"];
//PREPARE the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
//Prepare data which will contain the json request string.
NSData *requestData = [NSData dataWithBytes:[jsonRequest UTF8String] length:[jsonRequest length]];
//Set the propreties of the request
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [requestData length]] forHTTPHeaderField:#"Content-Length"];
[request setValue:jsonRequest forHTTPHeaderField:#"Query-string"];
//set the data prepared
[request setHTTPBody: requestData];
//Initialize the connection with request
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
//Start the connection
[delegate showIndicator];
[connection start];
}
//delegate methods:
//METHODS TO HANßDLE RESPONSE
#pragma mark NSURLConnection delegate methods
//WHen receiving the response
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#" Did receive respone");
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//While receiving the response data
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
//When failed just log
[delegate hideIndicator];
NSLog(#"Connection failed!");
NSLog(#"Error %#", error);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//When the response data is downloaded
// NSLog(#" Data obtained %#", responseData);
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
// NSLog(#" Response String %#", responseString);
//converted response json string to a simple NSdictionary
//If the response string is really JSONABLE I will have the data u sent me displayed succefully
NSMutableArray *results = [responseString JSONValue];
NSLog(#"Response: %#", results);
/// la la al alal alaa
}
It's your back end who should define how the response in Json will look like
Refer NSJsonSerialization class. Which is newly included in ios5.0. which is the most flexible one for your need. you can access it in apple developer site.
link to NSJSONSerialization

How to get an Answer from a web service after sending Get request in iOS

I'm a novice in iOS developing, and have some problems with understanding web service organization. I want to send a Get query to the URL. And I do it so:
-(BOOL) sendLoginRequest{
NSString *getAction = [NSString stringWithFormat:#"action=%#&username=%password=%#",#"login",self.log.text, self.pass.text];
NSString *getUserName = [NSString stringWithFormat:#"username=%#",self.log.text];
NSString *getPassword = [NSString stringWithFormat:#"username=%#",self.pass.text];
NSData *getDataAction = [getAction dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *getLengthAction = [NSString stringWithFormat:#"%d", [getDataAction length]];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:#"http:http://www.site.fi/api/"]];
[request setHTTPMethod:#"GET"];
[request setValue:getLengthAction forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:getLengthAction];
self.urlConnection = [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
NSAssert(self.urlConnection != nil, #"Failure to create URL connection.");
// show in the status bar that network activity is starting
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
the answer may be "true" or "false"
but how can I take this answer?
You should define next methods to get answer:
Start connection: [self.urlConnection start];
Check server response:
- (void)connection:(NSURLConnection *)theConnection didReceiveResponse:(NSURLResponse *)response
Collect data that servers sends you:
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)data
Be sure to manage errors:
- (void)connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)error
Check received data:
- (void)connectionDidFinishLoading:(NSURLConnection *)theConnection
To be more sure that you correctly understood me check NSURLConnection Class Reference
Send [self.urlConnection start]; and implement the NSURLConnectionDelegate methods to receive the response. Alternatively use ASIHTTPRequest and the block handlers, which to my way of thinking are much easier to write for beginners, provided you don't need to run on iOS pre-4.1.
You will gather the data returned as NSData; just convert that to a string, and either call boolValue on the string (check the docs for its rather strange tests), or use a specific set of your own tests.