如何从UIWebView获取最后的HTTP状态代码?

时间:2009-06-30 01:40:41

标签: iphone cocoa cocoa-touch

我想检测到给UIWebView的页面加载请求何时返回了5xx或4xx范围内的状态代码。

我已经为Web视图设置了委托,并提供了一个-webView:didFailLoadWithError:错误方法,但是虽然它被称为超时超时,但是没有调用HTTP状态代码错误。

有什么建议吗?

9 个答案:

答案 0 :(得分:10)

嗯......我不是iPhone开发者,但是......

您是否可以尝试使用要加载的网址创建NSURLRequest?然后你可以使用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

这似乎很多工作,但可以做到。希望其他人能提出更简单的方法,虽然我没有看到它。

答案 1 :(得分:4)

我努力寻找一个好的答案已经挣扎了很长一段时间。我正在工作的要求是我需要能够确定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

拦截所有请求并自行制作。您必须拥有某种请求管理技术,因为当您在UIWebView上调用loadData时,它将调用shouldStartLoadWithRequest回调,并且您希望确保不执行无限循环以发出相同的请求。结束了。

答案 2 :(得分:3)

现在,当事情发生在 Swift 3.0 时,我在这个话题上非常努力。我甚至创建了一个自定义URLProtocol,并尝试拦截所有Web请求,只是为了最终意识到它是不必要的。我感到困惑的原因是因为他们移动了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)
}

答案 3 :(得分:2)

目前,UIWebView不提供任何获取其加载的请求的HTTP状态代码的功能。一种解决方法是使用UIWebViewDelegate方法拦截UIWebView的请求加载过程,并使用NSURLConnection检测服务器如何响应该请求。然后你可以采取适合这种情况的适当行动。 This article详细解释了演示项目的解决方法。

收到回复后,您无需继续加载请求。在学习HTTP状态代码后,您可以在 - (void)连接:(NSURLConnection *)连接didReceiveResponse:(NSURLResponse *)响应方法中取消连接。这样可以防止连接加载任何不必要的响应数据。然后,您可以再次在UIWebView中加载请求,或者根据HTTP状态代码等向用户显示相应的错误消息。

Here is the article

and here is the demo project on github

答案 4 :(得分:2)

以下是获取HTTP响应代码的解决方法,但只向每个网址发送一个请求: -

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 :(得分:1)

正如我刚刚在another thread上发布的那样,也可以拦截NSURLRequest级别的任何NSURLProtocol并在那里创建NSURLResponse,而不是在你的{{1}} UIWebView委托/控制器。在我看来,这更好的原因是它维护了UIWebView的后/前导航堆栈。这个方法的大纲可以在Rob Napier的这篇优秀博客文章中找到:

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

以及GitHub上的代码:

https://github.com/rnapier/RNCachingURLProtocol

答案 6 :(得分:0)

这是一个很好的例子,他们使用组合为第一次加载创建NSURLConnection,并为下一页创建UI​​WebView:

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

基本上这是主要技巧,使用shouldStartLoadRequest的YES / NO返回值:

- (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;
}

这可以让你在不使用同步请求的情况下前进,尤其是与Jeff的回答相结合。

答案 7 :(得分:-4)

可悲的是,目前看来最好的选择是使用-stringByEvaluatingJavaScriptFromString:运行某种小脚本来查询文档的状态代码。

答案 8 :(得分:-6)

如何在webViewDidFinishLoad:上实施UIWebViewDelegate,并使用UIWebView的request属性访问您感兴趣的标题?像这样(未经测试):

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