且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

找不到webLink时,UIWebView没有转到didFailLoadWithError?

更新时间:2023-11-17 17:37:46

不幸的是,404(或类似代码)不被 UIWebView ,因为收到了HTML响应。更糟糕的是, UIWebView 不会捕获我们的响应代码,因此您必须通过 NSURLConnection 手动执行此操作。这是处理它的一种方法:

Unfortunately, 404 (or similar codes) aren't considered errors by UIWebView, because a HTML response was received. Worse, the UIWebView doesn't capture response codes for us, so you have to do that manually, via NSURLConnection. Here's one way to deal with it:

@interface ViewController () <UIWebViewDelegate, NSURLConnectionDataDelegate>

@property (nonatomic) BOOL validatedRequest;
@property (nonatomic, strong) NSURL *originalUrl;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // since `shouldStartLoadWithRequest` only validates when a user clicks on a link, we'll bypass that
    // here and go right to the `NSURLConnection`, which will validate the request, and if good, it will
    // load the web view for us.

    self.originalUrl = [NSURL URLWithString:@"http://www.***.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:self.originalUrl];
    [NSURLConnection connectionWithRequest:request delegate:self];
}

#pragma mark - UIWebViewDelegate

// you will see this called for 404 errors

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    self.validatedRequest = NO; // reset this for the next link the user clicks on
}

// you will not see this called for 404 errors

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    NSLog(@"%s error=%@", __FUNCTION__, error);
}

// this is where you could, intercept HTML requests and route them through
// NSURLConnection, to see if the server responds successfully.

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    // we're only validating links we click on; if we validated that successfully, though, let's just go open it
    // nb: we're only validating links we click on because some sites initiate additional html requests of
    // their own, and don't want to get involved in mediating each and every server request; we're only
    // going to concern ourselves with those links the user clicks on.

    if (self.validatedRequest || navigationType != UIWebViewNavigationTypeLinkClicked)
        return YES;

    // if user clicked on a link and we haven't validated it yet, let's do so

    self.originalUrl = request.URL;

    [NSURLConnection connectionWithRequest:request delegate:self];

    // and if we're validating, don't bother to have the web view load it yet ...
    // the `didReceiveResponse` will do that for us once the connection has been validated

    return NO;
}

#pragma mark - NSURLConnectionDataDelegate method

// This code inspired by http://www.ardalahmet.com/2011/08/18/how-to-detect-and-handle-http-status-codes-in-uiwebviews/
// Given that some ISPs do redirects that one might otherwise prefer to see handled as errors, I'm also checking
// to see if the original URL's host matches the response's URL. This logic may be too restrictive (some valid redirects
// will be rejected, such as www.adobephotoshop.com which redirects you to www.adobe.com), but does capture the ISP
// redirect problem I am concerned about.

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{     
    NSString *originalUrlHostName = self.originalUrl.host;
    NSString *responseUrlHostName = response.URL.host;

    NSRange originalInResponse = [responseUrlHostName rangeOfString:originalUrlHostName]; // handle where we went to "apple.com" and got redirected to "www.apple.com"
    NSRange responseInOriginal = [originalUrlHostName rangeOfString:responseUrlHostName]; // handle where we went to "www.***.com" and got redirected to "***.com"

    if (originalInResponse.location == NSNotFound && responseInOriginal.location == NSNotFound) {
        NSLog(@"%s you were redirected from %@ to %@", __FUNCTION__, self.originalUrl.absoluteString, response.URL.absoluteString);
    }

    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
        NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];

        if (statusCode < 200 || statusCode >= 300) {
            NSLog(@"%s request to %@ failed with statusCode=%d", __FUNCTION__, response.URL.absoluteString, statusCode);
        } else {
            self.validatedRequest = YES;

            [self.webView loadRequest:connection.originalRequest];
        }
    }

    [connection cancel];
}

@end

注意,在我的实施中,我不仅要检查状态代码,还要检查重定向(您可能想要也可能不想做)。我之所以这样做,是因为某些ISP拦截了HTTP请求,如果找不到目标站点,请将您重定向到他们自己的搜索网页(我认为我的ISP正在查看我搜索的每个网站,这有点令人毛骨悚然)。如果你正在处理通过wifi连接的iPhone,你必须处理这些变幻莫测的问题。

Note, in my implementation, I'm not only checking for status codes, but I'm also checking for redirects (which you may or may not want to do). I do this because some ISP's intercept HTTP requests, and if the destination site is not found, redirect you to their own search web page (which I think is a bit creepy knowing that my ISP is checking out every web site I search for). And if you're dealing with iPhones that are connecting via wifi, you have to deal with these vagaries.

因此,例如,我上面的代码正在搜索 http ://www.applecom/pages (其中我故意省略了.com的时期,其中应该无法进行DNS查找),但我的ISP,Verizon,截获请求并通过HTTP连接重定向到他们自己的搜索页面,因此,我的应用报告:

So, for example, my code above is searching for "http://www.applecom/pages" (in which I deliberately omitted the period of ".com", which should fail a DNS lookup), but for which my ISP, Verizon, intercepted the request and redirected by HTTP connection to their own search page and as such, my app is reporting:


2013-01-21 23 :14:21.896 webtest [24198:c07] - [ViewController连接:didReceiveResponse:]您是从 http:// www。重定向的。 applecom / pages http://search.dnsassist。 verizon.net/assist.php?url=www.applecom

您可能想要考虑哪种重定向是可以接受的(例如,如果你去www.adobephotoshop.com并将它重定向到w ww.adobe.com)以及不是什么类型(例如,如果我去www.applecom并将其重定向到search.dnsassist.verizon.net。我可能会担心一个相当狭窄的问题(因为我的ISP而影响了我),但这是需要考虑的事情。

You might want to think about what sort of redirects are acceptable (e.g., if you go to "www.adobephotoshop.com" and it redirects you to "www.adobe.com") and what sorts aren't (e.g., if I go to "www.applecom" and it redirects me to "search.dnsassist.verizon.net". I may be worrying about a fairly narrow problem (which affects me because of my ISP), but it's something to contemplate.