如何使用WKWebView检索文件?

36

有一个文件(CSV)我想要下载,但它位于一个需要登录的网站后台。我想展示一个WKWebView来让用户登录,然后在他们登录后应用程序可以下载该文件。

我尝试过让用户登录网站后在WKWebView之外下载该文件,但会被沙盒隔离,只会下载一个带有登录表单的HTML文档而非所需的文件。

我还尝试向WKUserContentController对象添加WKUserScript,但当加载非HTML文件时该脚本不会运行。

有没有一种方法让我能够访问这个文件并允许用户通过WKWebView登录?

5个回答

97

目前,WKWebView实例将忽略任何默认的网络存储(NSURLCache、NSHTTPCookieStorage、NSCredentialStorage)以及您可以使用的用于定制网络请求的标准网络类(NSURLProtocol等)。

因此,WKWebView实例的cookie不会存储在您的应用程序的标准Cookie存储中,因此仅使用标准Cookie存储的NSURLSession/NSURLConnection无法访问WKWebView的cookie(这可能是您遇到的问题:“登录状态”很可能存储在cookie中,但NSURLSession/NSURLConnection看不到该cookie)。

缓存、凭据等也是相同情况。WKWebView有自己的私有存储,因此与标准Cocoa网络类不兼容。

您还无法自定义请求(添加自己的自定义HTTP头、修改现有头等),使用自己的自定义URL模式等,因为WKWebView也不支持NSURLProtocol。

因此,目前对于许多应用程序而言,WKWebView相当无用,因为它不能与Cocoa的标准网络API合作。

我仍然希望苹果公司能够在发布iOS 8之前改变这一点,否则WKWebView将对许多应用程序而言毫无意义,并且我们可能需要再使用UIWebView一段时间。

因此,请向苹果发送错误报告,以便苹果了解这些问题的严重性并进行修复。


1
有趣的信息,你是怎么发现它的? - user454322
12
希望更多人在实施之前发现这篇文章。这一点并不明显,并且我认为这是一个严重的错误。请点赞此回复以帮助获得更多关注,并确保提交到bugreport.apple.com。 - Bao Lei
@BaoLei 刚刚浪费了4个小时在这上面。:( - Rudolf Adamkovič
1
截至iOS8.2 beta版本,尚未对WebKit2进行任何更改以解决所有这些问题。 - Léo Natan
@Alex :: 这些问题还存在吗? - Sourav Gupta
4
一些问题已经在iOS 9中得到解决。您现在可以使用“WKWebsiteDataStore”类来请求cookies、缓存等。对于某些应用程序,这可能足够了。但主要限制仍然存在:仍不支持NSURLProtocol,因此仍无法过滤和自定义WKWebView的网络流量。 - Alex

15

你是否检查了请求返回的响应cookie。你可以使用如下委托方法。

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
    NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];

    for (NSHTTPCookie *cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    }

    decisionHandler(WKNavigationResponsePolicyAllow);
}

2
没有使用它为我创建任何 cookie。 - Devin McKaskle
这对我有用。一旦我将此代码插入我的委托中,注销并重新登录(因为我发现cookie只能通过成功的登录通知响应下载,并且在那之后基本上无法访问),随后我就能够使用相同的会话通过NSURLSession下载文件。 - TiM

1
你可以通过Javascript获取cookie 链接
然后,你可以使用获得的cookie手动下载文件:
webView.evaluateJavaScript("(function() { return document.cookie })()", completionHandler: { (response, error) -> Void in
  let cookie = response as! String
  let request = NSMutableURLRequest(URL: docURL)
  request.setValue(cookie, forHTTPHeaderField:  "Cookie")

  NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data, response, error) in
    // Your CSV file will be in the response object
  }).resume()
})

抱歉,您的解决方案对我无效。我在响应中得到了500状态码。 - Yogendra Patel

1

我已经完成了类似于您正在尝试做的事情:

  • 像您已经在做的那样,使用Web视图进行登录
  • 在您的Web视图上设置一个navigationDelegate
  • 在您的代理中实现-webView:decidePolicyForNavigationResponse:decisionHandler
  • 当调用该代理方法(用户登录后),您可以检查navigationResponse.response(将其转换为NSHTTPURLResponse*),并查找包含身份验证会话所需信息的Set-Cookie标头。
  • 然后,您可以通过在请求中手动指定响应中指定的Cookie来下载CSV。

请注意,委托方法仅针对“主框架”请求调用。这意味着AJAX请求或内部框架将不会触发它。整个页面必须刷新才能使其工作。

如果您需要为AJAX请求、iframe等触发行为,则需要注入一些JavaScript。


这与Nick的答案有何不同?当时他回答时我尝试过,但对我没有用。 - Devin McKaskle

0

当我尝试从 userContentController didReceiveScriptMessage 发出请求时,似乎没有会话cookie。

但是,当从 decidePolicyForNavigationAction 调用时,以下代码会检测到我已登录。

     let urlPath: String = "<api endpoint at url at which you are logged in>"

    let url = NSURL(string: urlPath)
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in

        let string1 = NSString(data: data, encoding: NSUTF8StringEncoding)
        println(string1)
        println(data)
        if(error != nil) {
            println("Error sending token to server")
            // Print any error to the console
            println(error.localizedDescription)
        }
        var err: NSError?

    })

    task.resume()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接