How to detect and handle HTTP error codes in UIWeb

2020-02-23 07:39发布

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.

9条回答
爷的心禁止访问
2楼-- · 2020-02-23 07:57

In webViewDidFinishLoad:

if ([[(NSHTTPURLResponse*)[[NSURLCache sharedURLCache] cachedResponseForRequest:webView.request] valueForHTTPHeaderField:@"Status"] intValue] == 404){
}

You may consider this solution over other more complex ones, even though some responses might not get cached. Note that wrong urls are usually getting cached by a system which has default configurations.

查看更多
Melony?
3楼-- · 2020-02-23 07:59

Heres is my swift 3 version of @AxelGuilmin response:

func webViewDidFinishLoad(_ webView: UIWebView) {

        guard let request = webView.request else { return }

        let cachedUrlResponse = URLCache.shared.cachedResponse(for: request)
        let httpUrlResponse = cachedUrlResponse?.response as? HTTPURLResponse
        if let statusCode = httpUrlResponse?.statusCode {
            if statusCode == 404 {
                // Handling 404 response
            }
        }
    }
查看更多
叼着烟拽天下
4楼-- · 2020-02-23 08:00

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!

查看更多
时光不老,我们不散
5楼-- · 2020-02-23 08:01

I am new to iOS and Swift development, and needed to find a way to accomplish this as well, using WKWebView (not UI). I was fortunate enough to run across this site that gave me the following answer, which works perfectly for my needs. It might help passers-by that are looking for the same answer I was.

Using this function (from WKNavigationDelegate):

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void)

You can create custom responses based on the HTTP response, like so:

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {

    // get the statuscode
    guard let statusCode = (navigationResponse.response as? HTTPURLResponse)?.statusCode
    else {
        decisionHandler(.allow)
        return
    }

    // respond or pass-through however you like
    switch statusCode {
    case 400..<500:
        webView.loadHTMLString("<html><body><h1>You shall not pass!</h1></body></html>", baseURL: nil)
    case 500..<600:
        webView.loadHTMLString("<html><body><h1>Sorry, our fault.</h1></body></html>", baseURL: nil)
    default:
        print("all might be well")
    }

    decisionHandler(.allow)
}
查看更多
冷血范
6楼-- · 2020-02-23 08:16

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.

查看更多
戒情不戒烟
7楼-- · 2020-02-23 08:17

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,

查看更多
登录 后发表回答