How to handle NSURLRequest HTTP error 303? - iphone

When I run my NSURLRequest in Cocoa, I get a 303 HTTP error, which is a redirect. How can I pull the proper URL to redirect to? Is it in the error variable, or somewhere else?

You might want to check out the automatic handling of redirects with NSURLConnection:
Handling Redirects and other Request Changes
If you'd like to handle it manually, the redirect url is in the response's 'Location' header. Here's how you can grab it in your connection:didReceiveResponse delegate method.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
// ... if the response status is 303 ...
if ([response respondsToSelector:#selector(allHeaderFields)]) {
NSString* location = [[httpResponse allHeaderFields] valueForKey:#"Location"];
// do whatever with the redirect url
}
}

Related

Multiple URL calls in same class

I am new to iphone development could somebody help me out this problem ,From one week i am facing with one issue , that is i have multiple urls like below
for (int i=0;i<=[listingAffArray];i++)
NSString *urlStr=[NSString stringWithFormat:#"http://demo.holidayjuggle.net:7777/services/inventoryservice/%#/%#/stores/search?location=12.971598700000000000,77.594562699999980000,50",appDelegate.buyingAff,[appDelegate.listingAffArray objectAtIndex:i]];
}
in this i am getting response from all the urls but in didfinishloading could not able to find which urls responsedata
NSURL *url=[NSURL URLWithString:urlStr];
NSMutableURLRequest *req=[NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:#"GET"];
[req setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[req setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
connection=[NSURLConnection connectionWithRequest:req delegate:self];
if(connection){
NSLog(#"connection is successfull ");
}
else{
NSLog(#"connection failed");
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
responseData=[[NSMutableData alloc]init];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[responseData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *strResponse=[[NSString alloc]initWithData:responseData encoding:NSUTF8StringEncoding];
}
in responsedata only last url data is there i put breakpoint and observed each url is calling in didfinishloading , when the second url is calling it is upadating with the secondurl call like that in responsedata last url data only is there .How to store each response data seperately
Thanks in advance
sivakumari
Create an array in your class and store each strResponse in your array (using addObject).
Also, this doesn't make sense:
for (int i=0;i<=[listingAffArray];i++)
NSString *urlStr=[NSString stringWithFormat:#"http://demo.holidayjuggle.net:7777/services/inventoryservice/%#/%#/stores/search?location=12.971598700000000000,77.594562699999980000,50",appDelegate.buyingAff,[appDelegate.listingAffArray objectAtIndex:i]];
}
The [listingAffArray] part should give you a compiler error and, even if that did work, you would be overwriting the same variable each time through the loop.
Yes it happen because all NSURLConnection share same delegate same object which is "self"
If u want to load multiple URL than you should have multiple space to store that data.
So one object of responseData is not sufficient.
There can be many way let me suggest one which I use.
Declare a NSMutableDictionary object
Store NSURLConnection object as key and NSMutableData object as value so if u have 3 URL you have 3 entery in NSMutableDictionary.
In every Delegate method of NSURLConnection append data only to corresponding NSMutableData object.
Tell me if u need more help....
Try to call web service with url in a asynchronous manner.
ie.Hit the first url and when you received the result of first url the hit the second url request. after that when u received the second url response hit the third url request.
These all request should run in background thread or a new thread, so that it doesn't effect the main thread.
Also take a enum data type like
enum {
requestOne=0,
requestTwo,
requestThree,
requestFour
}currentRequest;
when u start hitting the first url in currentRequestData assign requestOne and in response check wit this enumDataType. when response received then hit second url with seconod enum type

NSURLRequest with multiple parameters

I am trying to set up a NSURLRequest to download a simple index.html with its externa style.css sheet but I am not quite sure how to do this.. I have only ever just formatted the URL of the request to the file I want.. but this has to be slightly different and I cannot find a good example of what I am trying to do.
this is my code so far:
#pragma mark - NSURLConnection methods
- (void)htmlRequest
{
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.mywebsite.com/index.html"]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [NSMutableData data];
} else {
// Inform the user that the connection failed.
NSLog(#"Connection Fail");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// inform the developer of error type
}
// This method uses methodName to determin which Initalizer method to send the response data to in EngineResponses.m
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// EngineResponses *engineResponses = [EngineResponses sharedManager];
// [engineResponses GetManufacturers:receivedData];
NSString *myString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
NSLog(#"%#", myString);
}
as you can see I am just calling index.html directly.. I would like to know how to format my request so i get the index.html as well as style.css
any help would be greatly appreciated.
I always create a new data structure,which has a -connection property and a -request property,like this
#interface connectionWrapper : NSObject
#property(retain) NSURLRequest *request
#property(retain) NSURLConnection *connection
by retaining this data structure in an mutable array, you can distinguish the connections in callback methods by iterate the array and compare each connectionWrapper instance's -connection property with the connection parameter the of the callback method, if they match(points to a same object), then you can retrieve the -request property of the connectionWrapper instance, then -url property of NSURLRequest instance.
as I'm not an native English speaker, I think code is a better tutor.
-(NSURLRequest*)getRequestByConnection:(NSURLConnection*)connection
{
for(connectionWrapper *w in theArrayContainingAllConnectionWrappers)
{
if(w == connection)
return w.request;
}
}
In callback method:
-(void)connection:(NSURLConnection*)connection didReceiveResponse(NSURLResponse*)response
{
NSURLRequest *request = [self getRequestByConnection:connection];
NSURL *url = [request url];
/*apply different approach to different url/*
}
PS:it's very sad that NSURLConnection don't have a -request property so that we can retrieve the request associated with the connection easily.
One way or another, you will have to make 2 requests. Even if you open a web page directly in a web browser, the browser will make a separate request for the CSS file referenced in the HTML it downloads. If your application needs both the HTML and the CSS file, then you want it to make 2 separate URL requests, first to get the HTML and then to get the CSS file.
Now, just because 2 requests need to be made, that doesn't mean you will always need to write the code that makes those 2 requests. It may be that libraries like the ones recommended by #Slee automatically take the results of a first request, parse them out, and make requests for any referenced CSS files. I have not worked with them so I am not sure what they support, or if any libraries will do this for you.
One thing you may want to consider is loading the HTML and CSS through a UIWebView rather than handling it all manually. UIWebView will attempt to load, parse, and render an HTML file into a UI component. In the process it will load referenced CSS and JavaScript files and apply them to its rendering. If you want to do anything special like intercept the calls it makes to load the CSS file(s), you can implement the UIWebViewDelegate protocol and set the delegate of the the UIWebView. Within that delegate you can implement the -webView:shouldStartLoadWithRequest:navigationType: method to be notified when the web view is loading the CSS file. You can use the call to that method to look at the request that is being issued for the CSS and do something else interesting with the request.
do you know the name of the .css file?
If so I would just make 2 requests otherwise you will have to write a parser to look for the link to the css and make a second request anyways.
I'd also suggest looking into a library to handle the downlading of stuff - lot's of great libraries that can do the heavy lifting for you with advanced features.
Here's 3 I have used:
http://blog.mugunthkumar.com/coding/ios-tutorial-advanced-networking-with-mknetworkkit/
https://github.com/tonymillion/TMHTTPRequest
https://github.com/pokeb/asi-http-request

NSUrlConnectionDelegate - Getting http status codes

in iOS, how can I receive the http status code (404,500 200 etc) for a response from a web server. I am assuming it's in the NSUrlConnectionDelegate.
Objective-C or Monotouch .NET answer ok.
Yes, you can get status code in delegate method -didRecieveResponse:
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int code = [httpResponse statusCode];
}
NSHTTPURLResponse* urlResponse = nil;
NSError *error = nil;
responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error];
The aSynchronous request should also have a way to get the NSHTTPURLResponse..
You get the status code like this:
int statusCode = [urlResponse statusCode];
int errorCode = error.code;
In the case of some much used error codes (like 404) it will get put in the error but with a different code (401 will be -1012).
Here's how to do it in MonoTouch for .NET for those C# users. THis is in the NSUrlConnectionDelegate.
public override void ReceivedResponse (NSUrlConnection connection, NSUrlResponse response)
{
if (response is NSHttpUrlResponse)
{
var r = response as NSHttpUrlResponse;
Console.WriteLine (r.StatusCode);
}
}
Looking at this other stackoverflow question it looks like you can handle http status codes in the - (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)response delegate method:
- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse*)response
{
if ([response isKindOfClass: [NSHTTPURLResponse class]])
statusCode = [(NSHTTPURLResponse*) response statusCode];
}

How do I get the last HTTP Status Code from a UIWebView?

I would like to detect when a page load request give to a UIWebView has returned a status code in the 5xx or 4xx range.
I've setup the delegate for the web view and have provided a -webView:didFailLoadWithError:error method but although it gets called fine for timeouts, it is not called for HTTP Status Code errors.
Any suggestions?
Hmmm... I'm not an iPhone developer, but....
Could you try creating an NSURLRequest with the URL you want to load? Then you could make the connection using NSURLConnection.
NSURLConnection has a delegate method
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
which will give the the response from the server. Please note that if you are making the connection over HTTP, the response will actually be of class NSHTTPURLResponse. The NSHTTPURLResponse can be used to get the status using the following instance method
- (NSInteger)statusCode
NSURLConnection has another delegate method
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
that can be used to get the data from the URL Connection. You could then manually load the data into your UIWebView using:
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
That seems like a ton of work, but it could be done. Hopefully someone else will come up with the easier way, though I don't see it.
I struggled with this for quite a while trying to find a good answer. The requirements that I was working under was that I needed to be able to determine the status of the FIRST page load, and any load after that I would assume that the user was clicking links which shouldn't be broken (not guaranteed, I know, but a lot better than the alternatives).
What I ended up doing was making the initial call myself via a NSURLConnection (synchronously), and then passing the data on to the UIWebView.
NSURL *googleURL = [NSURL URLWithString:#"http://www.google.com"];
NSURLRequest *googleRequest = [NSURLRequest requestWithURL:googleURL];
NSHTTPURLResponse *response;
NSError *error;
NSData *responseData = [NSURLConnection sendSynchronousRequest:googleRequest
returningResponse:&response
error:&error];
if ([response statusCode] >= 400 || error)
{
// handle error condition
} else {
[webView_ loadData:responseData MIMEType:[response MIMEType]
textEncodingName:[response textEncodingName]
baseURL:[response URL]];
[self setView:webView_];
}
If you desire to get the information for every request, you could simply use the method
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
to intercept all requests and make them yourself. You would have to have some kind of request management technique, because when you call the loadData on the UIWebView, it will invoke the shouldStartLoadWithRequest callback, and you want to make sure you don't do an infinite loop of making the same request over and over.
I struggled very hard on this topic when things are on Swift 3.0 now. I even created a custom URLProtocol and tried to intercept all web requests, just to realize eventually that it was unnecessary. The reason for the confusion for me is because that they moved the didReceiveResponse function:
optional public func connection(_ connection: NSURLConnection, didReceive response: URLResponse)
to NSURLConnectionDataDelegate, which inherits from NSURLConnectionDelegate.
Anyway, Here is the Swift 3.0 version that works:
// You first need to have NSURLConnectionDataDelegate on your UIWebView
// MARK: - get HTTP status code
// setup urlconnectiondelegate
// so that the didReceiveResponse is called
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
let conn: NSURLConnection? = NSURLConnection(request: request, delegate: self)
if conn == nil {
print("cannot create connection")
}
return true;
}
// intercept the actual http status code
func connection(_ connection: NSURLConnection, didReceive response: URLResponse) {
let httpResponse: HTTPURLResponse = response as! HTTPURLResponse;
print(httpResponse.statusCode)
}
Currently, UIWebView does not provide any functionality for getting HTTP status codes for the requests it loads. One workaround is to intercept the request loading process of UIWebView using the UIWebViewDelegate methods and use NSURLConnection to detect how the server responds to that request. Then you can take an appropriate action suitable for the situation. This article explains the workaround in detail on a demo project.
And you don't need to continue loading the request after you received a response. You can just cancel the connection in - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response method after learning the HTTP status code. This way you prevent the connection from loading any unnecessary response data. Then you can load the request in UIWebView again or show an appropriate error message to the user depending on the HTTP status code, etc.
Here is the article
and here is the demo project on github
Here's a work-around to get HTTP response code, but with sending just one request to each URL:-
BOOL isLoad;
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSLog(#"Requesting: %# - %d - %#", request.URL.absoluteString, navigationType, request.URL.host);
if (navigationType != UIWebViewNavigationTypeOther) {
//Store last selected URL
self.loadedURL = request.URL.absoluteString;
}
if (!isLoad && [request.URL.absoluteString isEqualToString:loadedURL]) {
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError || ([response respondsToSelector:#selector(statusCode)] && [((NSHTTPURLResponse *)response) statusCode] != 200 && [((NSHTTPURLResponse *)response) statusCode] != 302)) {
//Show error message
[self showErrorMessage];
}else {
isLoad = YES;
[_wbView loadData:data MIMEType:[response MIMEType]
textEncodingName:[response textEncodingName]
baseURL:[response URL]];
}
}];
return NO;
}
isLoad = NO;
return YES;
}
As I just posted on another thread, it is also possible to intercept any NSURLRequest at the level of the NSURLProtocol and create your NSURLResponse there, instead of in your UIWebView delegate/controller. The reason why this is preferable in my opinion is that it maintains the back/forward navigation stack of the UIWebView. The outline of the approach can be found in this excellent blog post by Rob Napier:
http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588
and there's code on GitHub:
https://github.com/rnapier/RNCachingURLProtocol
Here is a nice example where they use a combination of creating a NSURLConnection for the first loading, and the UIWebView for the next pages:
http://www.ardalahmet.com/2011/08/18/how-to-detect-and-handle-http-status-codes-in-uiwebviews/
Basically this is the main trick, using the YES/NO return value of shouldStartLoadRequest:
- (BOOL) webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
if (_sessionChecked) {
// session already checked.
return YES;
}
// will check session.
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
if (conn == nil) {
NSLog(#"cannot create connection");
}
return NO;
}
This should get you going without using synchronous requests, especially combined with Jeff's answer.
Sadly at present it looks like the best available option is to use -stringByEvaluatingJavaScriptFromString: to run some sort of small script that queries the document for its status code.
What about implementing webViewDidFinishLoad: on your UIWebViewDelegate, and using the request property of the UIWebView to access the headers you're interested in? Something like this (untested):
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString* theStatus = [[webView request] valueForHTTPHeaderField:#"Status"];
}

how to access mysql from iphone

i'm a beginner to iphone.i want to create a login page for my application.i cant figure out how to connect to a php page and retrieve corresponding data from mysql database to the iphone.could any one guide me how to go about it.
what does the iphone have to do with a connection between php and mysql ?
PHP will run with on a web server probably apache installed on some computer and it will connect to a MySQL db .. and u will access that php page from your iphone with a browser. Not sure what part will the iphone have in all this other than providing the browser
You might want to have a look at NSURLRequest which you can use with a NSURLConnection to send e.g. GET-Parameters to a URL. You can then implment the NSURLConnectionDelegate to respond to incoming data:
1) setup connection
receivedData =[NSMutableData data];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:url]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:20.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
2) Setup delegate methods in self:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response {
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *) response;
if([httpResponse statusCode]==200)
[receivedData setLength:0];
else
NSLog(#"Http-Reponse %u",[httpResponse statusCode]);
// HANDLE ERROR!
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// append the new data to the receivedData
// receivedData is declared as a method instance elsewhere
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// HANDLE THE CONNECTION ERROR
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// receivedData contains the data
// convert to string:
NSLog(#"finished loading: %#",[[[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding] autorelease]);
[connection release];
[receivedData release];
}
You'll want to expose the authentication functionality as a web service, then use the URL Loading code posted by Felix L. to initiate an actual connection to the web service.
You'll probably want to send a response from the server as XML, if so, you'll parse that response with an NSXMLParser, otherwise you can just send the response in whatever format you'd like and parse it appropriately.