How to detect and handle HTTP error codes in UIWebView?

33,270

Solution 1

You could capture the URLRequest here:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

and hand the request over to the delegate and return no. Then in the received response call from NSURLConnection cancel the connection and if everything is fine (check response) load the urlrequest once more in the webview. Make sure to return YES in the above call when loading the urlrequest again.

Not very elegant, but it might work.

Solution 2

My implementation inspired by Radu Simionescu's response :

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSCachedURLResponse *urlResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:webView.request];
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*) urlResponse.response;
    NSInteger statusCode = httpResponse.statusCode;
    if (statusCode > 399) {
        NSError *error = [NSError errorWithDomain:@"HTTP Error" code:httpResponse.statusCode userInfo:@{@"response":httpResponse}];
        // Forward the error to webView:didFailLoadWithError: or other
    }
    else {
        // No HTTP error
    }
}

It manages HTTP client errors (4xx) and HTTP server errors (5xx).

Note that cachedResponseForRequest returns nil if the response is not cached, in that case statusCode is assigned to 0 and the response is considered errorless.

Solution 3

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 (as suggested above). Then you can take an appropriate action suitable for the situation.

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.

and here is the demo project on github

Solution 4

NSURLConnection is the class you are looking for, I don't think this can be done directly in a UIWebView.

You can use the synchronous method

+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error

Or the Asynchronous ones. These are harder to setup as you have to append all the bits of data you get into the 1 NSData, but the end result is the same.


Regardless of if you use the Synchronous or Asynchronous methods:

If you get a NSError* object then there was a COMMS error. As noted in the other responses, this is NOT a HTTP status code but rather a communication problem.

If the connection succeeded, you get an NSURLResponse and NSData. Importantly the NSURLResponse for HTTP requests is actually the NSHTTPURLResponse subclass!

Then you must check the response to see what the error code is. Try this (where _responseInfo is your NSURLResponse object):

  NSInteger httpStatusCode = (NSHTTPURLResponse*)_responseInfo.statusCode;

responseInfo should always be a NSHTTPURLResponse for HTTP requests... but you might be wise to have an assert there just in case.

IF the statusCode is a success (i.e. 200) then your NSData object should contain the data of the response (whatever that may be). If the status code indicates an error then the NSData may contain a textual description of the error from the server.

NB. I really don't recommend tyring to parse the NSData object for the error message. That's what the HTTP 'statusCode' is for!

Solution 5

You're mis-interpreting what -didFailLoadWithError is for. Technically, the request succeeded. It was able to hit the server and find that the file you're requesting doesn't exist (i.e. 404). The -didFailLoadWithError method will get called if the server doesn't exist, for example. Your server exists. The file doesn't. The web view is not going to interpret errors in the content. The purpose of -didFailLoadWithError from the UIWebViewDelegate Apple Docs is:

Sent if a web view failed to load content.

From the Wikipedia article on HTTP 404:

The 404 or Not Found error message is a HTTP standard response code indicating that the client was able to communicate with the server but the server could not find what was requested. 404 errors should not be confused with "server not found" or similar errors, in which a connection to the destination server could not be made at all.

In all likelihood you'll have to parse the response text for a 404 which you could obtain with an NSURLConnection/NSURLRequest combination rather than a web view.

Best Regards,

Share:
33,270
EEE
Author by

EEE

Updated on July 22, 2022

Comments

  • EEE
    EEE almost 2 years

    I want to inform user when HTTP error 404 etc is received. How can I detect that? I've already tried to implement

    - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
    

    but it is not called when I receive 404 error.

  • EEE
    EEE about 14 years
    thanks for your response. I just misunderstood the didFailLoadError method.
  • EEE
    EEE about 14 years
    Thanks for your response. How can I search a string in html string?I am stuck at that.
  • Felix Lamouroux
    Felix Lamouroux about 14 years
    If you capture the request you can start your own NSUrlconnection and collect the received connection data, in connectionDidFinishLoading you can then have access to the html.
  • EEE
    EEE about 14 years
    Actually I already accessed the html, problem was searching a string in html in objective-c, but I have solved it in C. thanks.
  • Felix Lamouroux
    Felix Lamouroux about 14 years
    You may want to have a look at RegexKitLite to use Regular Expressions. BTW: if any of the answers here was helpful accept it by checking the box on the left.
  • William Denniss
    William Denniss almost 14 years
    parsing the response text for the error code is a bad idea, the response text could be anything! See my answer for the real way to get the HTTP status code.
  • Radu Simionescu
    Radu Simionescu about 10 years
    this means making every request twice... and that is wrong
  • GeneCode
    GeneCode over 7 years
    Radu is right. This is not good. Answer from Axel Guilmin at bottom is best I think.
  • GeneCode
    GeneCode over 7 years
    Thanks I think your answer is the most correct way to do it.
  • Hassy31
    Hassy31 over 7 years
    yes, you can receive status code but you are making request twice..? this is not good actually
  • Felix Lamouroux
    Felix Lamouroux over 7 years
    I know. As said above: not very elegant
  • Plasma
    Plasma about 7 years
    I was pulling my hair out till I tested this on a real device, the simulator always returns a 404 for me. It wasn't until I tried it on the iPad that it actually worked!!