Ответ 1
К сожалению, 404 (или аналогичные коды) не считаются ошибками UIWebView
, потому что получен ответ HTML. Хуже того, UIWebView
не отображает коды ответов для нас, поэтому вам нужно сделать это вручную, используя NSURLConnection
. Здесь один из способов справиться с этим:
@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.stackoverflow.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 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 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 host matches the response 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.stackoverflow.com" and got redirected to "stackoverflow.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-запросы, и если целевой сайт не найден, перенаправляйте вас на свою собственную веб-страницу поиска (что, по-моему, немного странно, зная, что мой интернет-провайдер проверяет каждый веб-сайт, который я ищу). И если вы имеете дело с iPhone, которые подключаются через Wi-Fi, вам приходится иметь дело с этими капризами.
Так, например, мой код выше ищет " http://www.applecom/pages" (в котором я умышленно пропустил период ". com", что должно привести к сбою DNS-поиска), но для которого мой интернет-провайдер Verizon перехватил запрос и перенаправил HTTP-соединение на свою собственную страницу поиска, и как таковое мое приложение сообщает:
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" и перенаправляет вас на "www.adobe.com" ), а какие нет (например, если я перейду к "www.applecom" и перенаправит меня на "search.dnsassist.verizon.net". Возможно, я беспокоюсь о довольно узкой проблеме (которая влияет на меня из-за моего провайдера), но это что-то для размышления.