Как получить последний код состояния HTTP из UIWebView?

Я хотел бы определить, когда запрос на загрузку страницы в UIWebView возвращает код состояния в диапазоне 5xx или 4xx.

Я установил делегат для веб-представления и предоставил метод -webView: didFailLoadWithError: error, но хотя он вызван отлично для тайм-аутов, он не вызывается для ошибок кода состояния HTTP.

Любые предложения?

Ответы

Ответ 1

Хммм... Я не разработчик iPhone, но....

Не могли бы вы попытаться создать NSURLRequest с URL-адресом, который хотите загрузить? Затем вы можете установить соединение с помощью NSURLConnection.

NSURLConnection имеет метод делегирования

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response

который даст ответ от сервера. Обратите внимание, что если вы выполняете соединение через HTTP, ответ будет действительно иметь класс NSHTTPURLResponse. NSHTTPURLResponse может использоваться для получения статуса с использованием следующего метода экземпляра

- (NSInteger)statusCode

NSURLConnection имеет другой метод делегата

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

который можно использовать для получения данных из URL-соединения. Затем вы можете вручную загрузить данные в свой UIWebView, используя:

- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL

Это похоже на массу работы, но это можно сделать. Надеюсь, кто-то еще придумает более простой способ, хотя я этого не вижу.

Ответ 2

Я боролся с этим довольно долго, пытаясь найти хороший ответ. Требования, над которыми я работал, заключались в том, что мне нужно было определить статус загрузки страницы FIRST, и любая загрузка после этого я бы предположил, что пользователь щелкал ссылками, которые не должны быть сломаны (не гарантировано, я знаю, но намного лучше, чем альтернативы).

То, что я закончил, - это сделать первый вызов через NSURLConnection (синхронно), а затем передать данные в UIWebView.

NSURL *googleURL = [NSURL URLWithString:@"http://www.google.com"];
NSURLRequest *googleRequest = [NSURLRequest requestWithURL:googleURL];
NSHTTPURLResponse *response;
NSError *error;
NSData *responseData = [NSURLConnection sendSynchronousRequest:googleRequest
                                             returningResponse:&response 
                                                         error:&error];

if ([response statusCode] >= 400 || error)
{
    // handle error condition
} else {
    [webView_ loadData:responseData MIMEType:[response MIMEType] 
                            textEncodingName:[response textEncodingName] 
                                     baseURL:[response URL]];
    [self setView:webView_];
}

Если вы хотите получить информацию для каждого запроса, вы можете просто использовать метод

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

чтобы перехватить все запросы и сделать их самостоятельно. У вас должен быть какой-то метод управления запросами, потому что, когда вы вызываете loadData в UIWebView, он будет вызывать обратный вызов shouldStartLoadWithRequest, и вы хотите, чтобы вы не делали бесконечный цикл выполнения одного и того же запроса и более.

Ответ 3

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

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

Вот статья

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

Ответ 4

Здесь можно найти код ответа HTTP, но с отправкой только запроса один на каждый URL-адрес: -

BOOL isLoad;
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    NSLog(@"Requesting: %@ - %d - %@", request.URL.absoluteString, navigationType, request.URL.host);
    if (navigationType != UIWebViewNavigationTypeOther) {
        //Store last selected URL
        self.loadedURL = request.URL.absoluteString;
    }
    if (!isLoad && [request.URL.absoluteString isEqualToString:loadedURL]) {
        [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
            if (connectionError || ([response respondsToSelector:@selector(statusCode)] && [((NSHTTPURLResponse *)response) statusCode] != 200 && [((NSHTTPURLResponse *)response) statusCode] != 302)) {
                //Show error message
                [self showErrorMessage];
            }else {
                isLoad = YES;
                [_wbView loadData:data MIMEType:[response MIMEType]
                 textEncodingName:[response textEncodingName]
                          baseURL:[response URL]];
            }
        }];
        return NO;
    }
    isLoad = NO;
    return YES;
}

Ответ 5

Как я только что разместил на другом потоке, также можно перехватить любой NSURLRequest на уровне NSURLProtocol и создать NSURLResponse там, а не в вашем делете/контроллере UIWebView. По моему мнению, предпочтительнее, чтобы он поддерживал задний/передний навигационный стек UIWebView. Очерк подхода можно найти в этом превосходном сообщении в блоге Роба Напира:

http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588

и там код на GitHub:

https://github.com/rnapier/RNCachingURLProtocol

Ответ 6

Я очень сильно боролся с этой темой, когда сейчас ситуация на Swift 3.0. Я даже создал собственный URLProtocol и попытался перехватить все веб-запросы, чтобы в конечном итоге понять, что это не нужно. Причина путаницы для меня заключается в том, что они перемещают функцию didReceiveResponse:

optional public func connection(_ connection: NSURLConnection, didReceive response: URLResponse)

до NSURLConnectionDataDelegate, который наследуется от NSURLConnectionDelegate.

В любом случае, вот версия Swift 3.0, которая работает:

// You first need to have NSURLConnectionDataDelegate on your UIWebView

// MARK: - get HTTP status code

// setup urlconnectiondelegate
// so that the didReceiveResponse is called
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
    let conn: NSURLConnection? = NSURLConnection(request: request, delegate: self)
    if conn == nil {
        print("cannot create connection")
    }
    return true;
}

// intercept the actual http status code
func connection(_ connection: NSURLConnection, didReceive response: URLResponse) {
    let httpResponse: HTTPURLResponse = response as! HTTPURLResponse;
    print(httpResponse.statusCode)
}

Ответ 7

Вот хороший пример, где они используют комбинацию создания NSURLConnection для первой загрузки и UIWebView для следующих страниц:

http://www.ardalahmet.com/2011/08/18/how-to-detect-and-handle-http-status-codes-in-uiwebviews/

В основном это основной трюк, используя возвращаемое значение YES/NO значения shouldStartLoadRequest:

- (BOOL) webView:(UIWebView *)webView 
     shouldStartLoadWithRequest:(NSURLRequest *)request 
                 navigationType:(UIWebViewNavigationType)navigationType
{
    if (_sessionChecked) {
        // session already checked.
        return YES;
    }

    // will check session.

    NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
    if (conn == nil) {
        NSLog(@"cannot create connection");
    }
    return NO;
}

Это должно заставить вас идти без использования синхронных запросов, особенно в сочетании с ответом Джеффа.

Ответ 8

К сожалению, в настоящее время похоже, что лучшим вариантом является использование -stringByEvaluatingJavaScriptFromString: для запуска небольшого script, который запрашивает документ для своего кода состояния.

Ответ 9

Как реализовать webViewDidFinishLoad: на UIWebViewDelegate и используя свойство request UIWebView для доступа к интересующим вас заголовкам? Что-то вроде этого (untested):

- (void)webViewDidFinishLoad:(UIWebView *)webView {
  NSString* theStatus = [[webView request] valueForHTTPHeaderField:@"Status"];
}