Как обнаружить и обработать коды ошибок HTTP в UIWebView?
Я хочу сообщить пользователю, когда получена ошибка HTTP 404 и т.д. Как я могу это обнаружить?
Я уже пытался реализовать
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
но он не вызывается, когда я получаю ошибку 404.
Ответы
Ответ 1
Здесь вы можете захватить URLRequest:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
и передать запрос делегату и вернуть его. Затем в полученном ответе ответа от NSURLConnection
отмените соединение, и если все в порядке (проверьте ответ), загрузите urlrequest еще раз в webview. Обязательно верните YES в приведенном выше вызове при повторной загрузке urlrequest.
Не очень элегантный, но он может работать.
Ответ 2
Моя реализация вдохновлена ответом Radu Simionescu:
- (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
}
}
Он управляет ошибками HTTP-клиента (4xx) и ошибками HTTP-сервера (5xx).
Обратите внимание, что cachedResponseForRequest
возвращает nil
, если ответ не кэшируется, в этом случае statusCode
присваивается 0, и ответ считается ошибочным.
Ответ 3
UIWebView не предоставляет никаких функций для получения кодов состояния HTTP для запросов, которые он загружает. Одним из обходных путей является перехват процесса загрузки запроса UIWebView с использованием методов UIWebViewDelegate и использование NSURLConnection для определения того, как сервер отвечает на этот запрос (как предложено выше). Затем вы можете предпринять соответствующее действие, соответствующее ситуации.
И вам не нужно продолжать загружать запрос после получения ответа. Вы можете просто отменить соединение в соединении - (void): (NSURLConnection *) connection didReceiveResponse: (NSURLResponse *) response метод ответа после изучения кода состояния HTTP. Таким образом вы предотвращаете загрузку соединения ненужных данных ответа. Затем вы можете снова загрузить запрос в UIWebView или показать пользователю соответствующее сообщение об ошибке в зависимости от кода состояния HTTP и т.д.
и вот демонстрационный проект на github
Ответ 4
Вы неправильно интерпретируете, что такое -didFailLoadWithError. Технически запрос был выполнен успешно. Он смог попасть на сервер и обнаружить, что файл, который вы запрашиваете, не существует (например, 404). Метод -didFailLoadWithError будет вызван, если сервер не существует, например. Ваш сервер существует. В файле нет. Веб-представление не будет интерпретировать ошибки в контенте. Цель -didFailLoadWithError из UIWebViewDelegate Apple Docs:
Отправлено, если веб-представление не загрузилось содержание.
Из статьи Википедии о HTTP 404:
Сообщение об ошибке 404 или не найдено стандартный код ответа HTTP что клиент смог связь с сервером, но сервер не смог найти то, что было просил. 404 ошибок не должно быть путать с "сервером не найденным" или аналогичные ошибки, в которых соединение на целевой сервер не может быть сделанный вообще.
По всей вероятности, вам придется проанализировать текст ответа для 404, который вы могли бы получить с помощью комбинации NSURLConnection/NSURLRequest, а не с веб-представлением.
С наилучшими пожеланиями,
Ответ 5
NSURLConnection - это класс, который вы ищете, я не думаю, что это можно сделать непосредственно в UIWebView.
Вы можете использовать синхронный метод
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
Или асинхронные. Их сложнее настроить, поскольку вам нужно добавить все биты данных, которые вы получаете в 1 NSData, но конечный результат одинаков.
Независимо от того, используете ли вы синхронные или асинхронные методы:
Если вы получаете объект NSError *, тогда была ошибка COMMS. Как отмечено в других ответах, это НЕ код состояния HTTP, а скорее проблема связи.
Если соединение выполнено успешно, вы получите NSURLResponse и NSData. Важно, что NSURLResponse для HTTP-запросов - это подкласс NSHTTPURLResponse!
Затем вы должны проверить ответ, чтобы узнать, что представляет собой код ошибки. Попробуйте (где _responseInfo
- ваш объект NSURLResponse):
NSInteger httpStatusCode = (NSHTTPURLResponse*)_responseInfo.statusCode;
responseInfo всегда должен быть запросом NSHTTPURLResponse для HTTP-запросов... но вам может быть разумно утверждать, что на всякий случай.
ЕСЛИ код состояния имеет успех (т.е. 200), то ваш объект NSData должен содержать данные ответа (независимо от того, что может быть). Если код состояния указывает на ошибку, то NSData может содержать текстовое описание ошибки с сервера.
NB. Я действительно не рекомендую tyring анализировать объект NSData для сообщения об ошибке. Это то, что для HTTP 'statusCode' для!
Ответ 6
В webViewDidFinishLoad:
if ([[(NSHTTPURLResponse*)[[NSURLCache sharedURLCache] cachedResponseForRequest:webView.request] valueForHTTPHeaderField:@"Status"] intValue] == 404){
}
Вы можете рассмотреть это решение по сравнению с другими более сложными, хотя некоторые ответы могут не кэшироваться. Обратите внимание, что неправильные URL-адреса обычно кэшируются системой, которая имеет конфигурации по умолчанию.
Ответ 7
Я новичок в разработке под iOS и Swift, и мне нужно было найти способ сделать это, используя WKWebView (не пользовательский интерфейс). Мне посчастливилось наткнуться на этот сайт, который дал мне следующий ответ, который идеально подходит для моих нужд. Это может помочь прохожим, которые ищут тот же ответ, что и я.
Используя эту функцию (из WKNavigationDelegate):
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void)
Вы можете создавать собственные ответы на основе HTTP-ответа, например так:
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)
}
Ответ 8
Heres - моя быстрая 3 версия ответа @AxelGuilmin:
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
}
}
}
Ответ 9
Я использовал ниже код для своего решения
-(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
NSLog(@"error :%@",error);
NSString *strError = [NSString stringWithFormat:@"%@",error];
if ([strError rangeOfString:@"Code=-1005"].location == NSNotFound) {
NSLog(@"string does not contain Code=-1005");
} else
NSLog(@"string contains Code=-1005");
}