Как обнаружить и обработать коды ошибок HTTP в UIWebView? - PullRequest
16 голосов
/ 10 февраля 2010

Я хочу сообщить пользователю, когда получена ошибка HTTP 404 и т. Д. Как я могу это обнаружить? Я уже пытался реализовать

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

но он не вызывается при получении ошибки 404.

Ответы [ 9 ]

19 голосов
/ 31 октября 2014

Моя реализация вдохновлена ​​ответом Раду Симионеску:

- (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 и ответ считается безошибочным.

8 голосов
/ 19 августа 2011

UIWebView не предоставляет никаких функций для получения кодов состояния HTTP для запросов, которые он загружает. Одним из обходных путей является перехват процесса загрузки запроса UIWebView с использованием методов UIWebViewDelegate и использование NSURLConnection для определения того, как сервер отвечает на этот запрос (как предложено выше). Тогда вы можете предпринять соответствующее действие, подходящее для ситуации. Эта статья подробно описывает обходной путь для демонстрационного проекта.

И вам не нужно продолжать загружать запрос после получения ответа. Вы можете просто отменить соединение в - (void) соединение: (NSURLConnection *) connection didReceiveResponse: (NSURLResponse *) ответ метод после изучения кода состояния HTTP. Таким образом вы предотвращаете загрузку соединения ненужных данных ответа. Затем вы можете снова загрузить запрос в UIWebView или показать пользователю соответствующее сообщение об ошибке в зависимости от кода состояния HTTP и т. Д.

Вот статья

и вот демонстрационный проект на github

7 голосов
/ 07 июня 2010

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-запросов ... но вам может быть целесообразно иметь там утверждение на всякий случай.

Если statusCode является успешным (т.е. 200), тогда ваш объект NSData должен содержать данные ответа (независимо от того, что это может быть). Если код состояния указывает на ошибку, то NSData может содержать текстовое описание ошибки с сервера.

NB. Я действительно не рекомендую tyring для анализа объекта NSData для сообщения об ошибке. Вот для чего нужен HTTP 'statusCode'!

7 голосов
/ 10 февраля 2010

Вы неправильно интерпретируете, для чего предназначен -didFailLoadWithError. Технически запрос выполнен успешно. Он смог подключиться к серверу и обнаружить, что запрашиваемый файл не существует (то есть 404). Например, метод -didFailLoadWithError будет вызываться, если сервер не существует. Ваш сервер существует. Файл не Веб-представление не будет интерпретировать ошибки в контенте. Цель -didFailLoadWithError из UIWebViewDelegate Apple Docs:

Отправляется, если не удалось загрузить веб-представление содержание.

Из статьи в Википедии HTTP 404 :

Сообщение об ошибке 404 или не найдено стандартный код ответа HTTP указывая, что клиент был в состоянии общаться с сервером, кроме сервер не смог найти что было просил. 404 ошибок быть не должно перепутано с "сервер не найден" или похожие ошибки, при которых происходит соединение на сервер назначения не может быть сделано на всех.

По всей вероятности, вам придется анализировать текст ответа для 404, который вы можете получить с помощью комбинации NSURLConnection / NSURLRequest, а не веб-просмотра.

С наилучшими пожеланиями,

7 голосов
/ 10 февраля 2010

Вы можете получить URLRequest здесь:

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

и передать запрос делегату и вернуть номер. Затем в полученном ответном вызове от NSURLConnection отмените соединение и, если все в порядке (проверьте ответ), снова загрузите URL-запрос в веб-просмотр. При повторной загрузке urlrequest обязательно верните YES в приведенном выше вызове.

Не очень элегантно, но это может сработать.

2 голосов
/ 27 апреля 2017

Вот мой быстрый 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
            }
        }
    }
2 голосов
/ 27 марта 2014

В webViewDidFinishLoad:

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

Вы можете рассмотреть это решение поверх других, более сложных, хотя некоторые ответы могут не кэшироваться. Обратите внимание, что неправильные URL-адреса обычно кэшируются системой, которая имеет конфигурации по умолчанию.

1 голос
/ 04 января 2019

Я новичок в разработке под 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)
}
0 голосов
/ 10 ноября 2014

Я использовал приведенный ниже код для моего решения

-(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”);

}

...