Как получить последний код состояния 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"];
}