how to fetch page content of an URL Request (post) - objective-c

I have a little Mac application which should be able to post Data to my web server which saves the data in a database. Now that's the Code I have now:
NSData *postData = [urlString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"http://..."]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection) NSLog(#"Done");
And it works fine. But now I want to check whether the data was correct (and stored in the database) or something (like eMail) is wrong. The PHP file prints e.g. "email incorrect" out if the E-Mail is not correct.
But how can I fetch this data (which PHP prints out) in Xcode that the App knows whether it was successful or not?
Thanks for answers!

You need to implement the NSURLConnectionDelegate methods connection:didReceiveData: and connectionDidFinishLoading:
According to the docs didReceiveData: may be called multiple times per NSURLRequest (i.e. the response will not always arrive all at once) so the recommended method is to append the incoming data to buffer during connection:didReceiveData: and then do any processing on the data in connectionDidFinishLoading:.
You could create a property on your class like this:
#property (nonatomic, strong) NSMutableData *dataBuffer;
And instantiate your buffer during viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataBuffer = [[NSMutableData alloc] init];
// do any other setup your class requires...
}
And then implement the delegate methods:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// clear the buffer in case it has been used previously
[self.dataBuffer setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.dataBuffer appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSString *response = [NSString stringWithUTF8String:[self.dataBuffer bytes]]);
NSLog(#"response from HTTP request=>%#", response);
}
This can all also be done using a third-party networking library like ASIHTTPRequest (which is no longer under active develoment) or AFNetworking, but sometimes those can be overkill depending upon what you are trying to accomplish

Implement the delegate method for the NSURLConnection,
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response.
This method is called by the app when the request finishes. You can access response data using the 'response' parameter.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
Then just convert the 'data' parameter into a string using:
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
Then you can search the response for whatever string you want, e.g., "email incorrect".
PS: I generally don't use NSURLConnection/NSURLRequest for HTTP requests, I'd recommend you check out ASIHTTPRequest for really simple HTTP requests/connections.
Hope this helps.

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?

Connecting to a RESTful webservice in objective-c

I tried connecting to a web service and retrieve data from it, but I did not succeed in doing so. Here is what I have done.
This is the web service I'm trying to connect to http://www.rcsb.org/pdb/software/rest.do
Here is my example.h file:
#import <Foundation/Foundation.h>
#interface example : NSObject {
NSMutableData *receivedData;
}
#property(nonatomic, retain) NSMutableData *receivedData;
- (void) getDataFromServer;
#end
Here is my example.m file:
import "example.h"
#implementation example
#synthesize receivedData;
-(void) getDataFromServer {
//prepare request
NSString *urlString = [NSString stringWithFormat:#"http://www.rcsb.org/pdb/rest/search/"];
NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc] init];
[theRequest setURL:[NSURL URLWithString:urlString]];
[theRequest setHTTPMethod:#"POST"];
NSString *myXmlQuery = [[NSString alloc] initWithFormat:#"<?xml version=\"1.0\" encoding=\"UTF-8\"?><orgPdbQuery><version>head</version><queryType>org.pdb.query.simple.AdvancedKeywordQuery</queryType><description>Text Search for: chloro</description><keywords>chloro</keywords></orgPdbQuery>"];
//set Headers
NSString *contentType = [NSString stringWithFormat:#"application/xml"];
[theRequest addValue:contentType forHTTPHeaderField:#"Content-Type"];
[theRequest addValue:[NSString stringWithFormat:#"%ld", [myXmlQuery length]] forHTTPHeaderField:#"Content-Length"];
//create the Body
NSMutableData *postBody = [NSMutableData data];
[postBody appendData:[[NSString stringWithFormat:#"<xml>"] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[myXmlQuery dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:#"</xml>"] dataUsingEncoding:NSUTF8StringEncoding]];
//post
[theRequest setHTTPBody:postBody];
NSLog(#"%#", myXmlQuery);
//get response
NSHTTPURLResponse *urlResponse = [[NSHTTPURLResponse alloc] init];
NSError *error = [[NSError alloc] init];
NSData *responseData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&urlResponse error:&error];
NSString *result = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"Response code- %ld",[urlResponse statusCode]);
NSLog(#"Response: %#", result);
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"in didReceiveResponse....setting receivedData to zero");
//[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"In didReceiveData...receiving data and appending to receivedData");
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Connection failed with error");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"in connectionDidFinishLoading");
NSLog(#"Succeeded! Received %ld bytes of data", [receivedData length]);
}
#end
I'm getting a html page as a response to this. Moreover, connectionDidFinishLoading is not being invoked any time because I'm not getting the NSLog statements in my output. All I am getting is the html version of the url I'm trying to connect to.
Any kind of help will be appreciated. Thank you.
PS: I'm trying to format this text properly but it is automatically getting formatted like this. Sorry for the inconvenience.
It looks like you are trying to do two different things. Firstly, I suggest you look at the URL Loading System Programming Guide. You also do not need to create a NSHTTPURLResponse* as that gets taken care of for you.
Secondly, you are trying to use the NSURLConnectionDelegate protocol without conforming to it, or adding your class as a delegate. This differs greatly from doing sendSynchronousRequest which will block the main thread, and does not need the delegate set because that method will wait for a response and not allow any other code to execute (hence blocking the thread). There's a really nice StackOverflow question and answer very similar to yours.
In a nutshell:
Conform to <NSURLConnectionDelegate> in your interface.
Create a property for a NSURLConnection*
Use self.myConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
Now you can rely on the NSURLConnectionDelegate protocol methods being called (connectionDidFinishLoading:, connection:didReceiveData:, etc).
Once you hit connectionDidFinishLoading: you can start using the data you downloaded.

difference between nsurlconnection and browser access?

I am developing a very simple application which accesses a written url. So i am wondering what is the difference between access by nsurlconnection and access by just using browser. cause some sites respond but they don`t send data when i used the nsurlconnection.
- (void)getWikiData:(NSString *)keyword{
NSString* tmpURL = #"http://wikipedia.simpleapi.net/api?keyword=";
NSString* encodedString;
CFStringRef strRef = CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)keyword, NULL, (CFStringRef)#"!*'();:#&=+$,/?%#[]~", kCFStringEncodingUTF8);
encodedString = [NSString stringWithString:(NSString *)strRef];
CFRelease(strRef);
[tmpURL stringByAppendingString:encodedString];
[tmpURL stringByAppendingString:#"&output=html"];
NSURL *url = [NSURL URLWithString:tmpURL];
NSString *userAgent = #"Custom User Agent";
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:url] autorelease];
[request setValue:userAgent forHTTPHeaderField:#"User-Agent"];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response{
NSLog(#"Receive Response");
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
NSLog(#"Receive Data");
}
Thanks in advance.
The difference is in the user agent string of the resulting application. MobileSafari reports itself as "Safari, iOS like Mac OS X", however, a plain NSURLConnection sends a CFNetwork description, which is not very useful for most sites to do 'browser' (rather 'client') detection, that's why they may refuse to send data to an unrecognized user agent.

How to update data on server db through json api?

i'm working on an iPhone application which retrieve sqlite database from server through json/rest api. And user can add rows to its tables locally and can update it locally. Now, as i added some rows to tables in local database, i want to sync/insert only those new rows to server database from my local updated db.
Please help if somebody knows about that api method(json/rest) or If there is any tutorial related to it please help.
When you say you are retrieving the "sqlite" database, do you mean a "json" representation of all the tables and their rows? I'm assuming you're not actually sending the "sqlite" db file.
For sending and retrieving json via http you can use NSURLConnection and NSURLRequest for simplicity, because they are built in. If you want to enforce a mapping to core data, you can use the RestKit framework for both the connection and data handling.
Here is an example implementation of the former solution - it assumes you are ARC, you will need to add the appropriate retain and release statements otherwise.
1) declare the class you're using as the appropriate delegate
#interface ClassName : NSObject <NSURLConnectionDelegate>
2) declare a responseData object that will be used to receive data
//interface
#property (nonatomic, strong) NSMutableData *responseData;
//implementation
#synthesize responseData;
3) create the function that sends the json request
- (void)sendRequest
{
responseData = [NSMutableData data];
//whatever your server address is
NSURL *url = [NSURL URLWithString:#"http://www.resturl.com/whatever"];
//just sample data - create this dictionary with what you want to send
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:#"SomeValue" forKey:#"SomeKey"];
NSError *jsonError;
//NSJSONSerialization is Apple's new json serialization class so we can use it to convert to and from json and foundation objects
NSData *requestdata = [NSJSONSerialization dataWithJSONObject:params options:0 error:&jsonError];
NSMutableURLRequest *request;
request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", [requestdata length]] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:requestdata];
//this kicks off the request asynchronously
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
//if you'd rather send a synchronous request, you can use the static NSURLConnection function
//sendSynchronousRequest:returningResponse:error:
}
4)implement the delegate functions to receive our data
//any time a piece of data is received we will append it to the responseData object
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.responseData appendData:data];
}
//some sort of error, you can print the error or put in some other handling here, possibly even try again but you will risk an infinite loop then unless you impose some sort of limit
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// Clear the activeDownload property to allow later attempts
self.responseData = nil;
}
//connection has finished, thse requestData object should contain the entirety of the response at this point
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError *jsonError;
NSDictionary *responseDict =
[NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONWritingPrettyPrinted
error:&jsonError];
if(responseDict)
{
NSLog(#"%#", responseDict);
}
else
{
NSLog(#"%#", [jsonError description]);
}
//clear out our response buffer for future requests
self.responseData = nil;
}
If you want to update the remote database with some new information, just keep track of the new rows locally (rather than just merging them with the full dataset) and send a new request containing only those rows to an endpoint that will add them. That is the simplest way to do this without enforcing an actual mapping.

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.