【问题标题】:How do I get the last HTTP Status Code from a UIWebView?如何从 UIWebView 获取最后一个 HTTP 状态代码?
【发布时间】:2010-11-06 21:02:44
【问题描述】:

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

我已经为 Web 视图设置了委托,并提供了一个 -webView:didFailLoadWithError:error 方法,但是虽然它可以正常调用超时,但不会调用 HTTP 状态代码错误。

有什么建议吗?

【问题讨论】:

  • HTTP 状态码错误不会调用它,因为 URL 加载系统不会将它们视为错误。连接本身很好,它返回了您的应用程序希望将其视为错误的代码,但并非所有代码都可能

标签: iphone cocoa cocoa-touch


【解决方案1】:

现在在 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)
}

【讨论】:

  • 似乎可以工作 - 虽然有些响应不是 HTTPURLResponse:s 所以对我来说崩溃了 - 我只是通过适当的可选检查避免了这种情况if let httpresponse = response as? HTTPURLResponse { /.../
【解决方案2】:

这是获取 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;
}

【讨论】:

    【解决方案3】:

    这是一个很好的例子,他们结合使用创建第一次加载的 NSURLConnection 和用于下一页的 UIWebView:

    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 的回答。

    【讨论】:

      【解决方案4】:

      正如我刚刚在another thread 上发布的那样,也可以在NSURLProtocol 级别拦截任何NSURLRequest 并在那里创建您的NSURLResponse,而不是在您的UIWebView 委托/控制器中。我认为这更可取的原因是它维护了 UIWebView 的后退/前进导航堆栈。可以在 Rob Napier 的这篇出色的博客文章中找到该方法的概要:

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

      GitHub上有代码:

      https://github.com/rnapier/RNCachingURLProtocol

      【讨论】:

        【解决方案5】:

        我为此苦苦挣扎了很长一段时间,试图找到一个好的答案。我正在处理的要求是我需要能够确定第一个页面加载的状态,之后的任何加载我都会假设用户正在点击不应该被破坏的链接(不保证,我知道,但比替代品好很多)。

        我最终做的是通过 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 回调,并且你要确保你不会做一个无限循环来发出相同的请求结束了。

        【讨论】:

        • 不适用于 UIWebView,但在其他部分我会尝试使用这几行代码,它似乎完全符合我的需求,并带有同步调用。谢谢
        • 谨慎使用同步请求。它确实阻止了 UI IIRC。
        【解决方案6】:

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

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

        Here is the article

        and here is the demo project on github

        【讨论】:

        • 文章链接现已严重损坏。
        • 如果服务器由于某种原因没有为两个请求返回相同的状态码怎么办?
        【解决方案7】:

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

        【讨论】:

        • 我认为不可能从 DOM 中检索状态码,它在某处吗?
        • 我不得不承认我对 Javascript 的了解还不够,无法说这两种方式。只是目前没有 Obj-C API
        • HTTP 是一种协议。 HTML 是一种标记语言。 HTTP 状态代码是 HTTP 响应标头的一部分。你不会在 DOM 中的某个地方找到 HTTP 状态代码。
        【解决方案8】:

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

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

        【讨论】:

        • 错了。为什么请求对象会包含服务器返回的状态?
        • 是的,这是错误的。请求和响应是两个不同的东西。
        【解决方案9】:

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

        您可以尝试使用要加载的 URL 创建一个 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
        

        这似乎是一项艰巨的工作,但可以做到。希望其他人会想出更简单的方法,虽然我没有看到。

        【讨论】:

        • 这当然是一种可能的解决方案。这意味着做同样的工作两次,虽然很遗憾。
        • 不知道为什么这被否决了。当然,这并不容易,但它会奏效。事实上,它(可能)是唯一的“官方”方式。
        • 再看一遍,这真的没有那么难。网上有大量关于如何设置 NSURLConnection 然后使用委托方法获取数据的示例代码。试试developer.apple.com/documentation/Cocoa/Conceptual/…
        • 请注意,didReceiveData: 不会给您的委托 数据;它为您的委托提供一些数据。您需要收集整个数据,直到连接完成,或者想办法将这些增量块提供给 UIWebView。
        • 可以直接hook到UIWebView加载中,看我下面的回复...
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-05
        • 2010-11-21
        • 2014-05-01
        相关资源
        最近更新 更多