JavaFX WebEngine等待ajax完成

 2023-02-17    396  

问题描述

我正在JAVAFX中开发一个数据挖掘应用程序,该应用程序依赖于WebView(也是WebEngine).采矿以2个步骤进行:首先,用户使用UI导航到WebView中的网站以配置可以在其中搜索有趣的数据.其次,使用定期运行的背景任务,WebEngine加载相同的文档并尝试从已加载文档中提取数据.

这在大多数情况下都非常有效,但是最近我在使用Ajax呈现内容的页面上遇到了一些麻烦.要检查WebEngine是否已加载文档,我会收听loadWorker’S stateProperty.如果状态过渡到成功,我知道该文档已加载(以及可能在document..ready()或等价上运行的任何JavaScript).这是因为JavaScript如果我没记错的话在javafx线程上执行( .oracle.com/javafx/entry/communcommating_between_javascript_and_javafx ).但是,如果启动了AJAX调用,则JavaScript执行完成,引擎让我知道文档已经准备就绪,尽管显然不是因为目录由于出色的Ajax调用而可能仍会更改.

JavaFX WebEngine等待ajax完成

.

是否有任何方法可以注入钩子,所以当Ajax调用完成后,我会收到通知吗?我尝试在$.ajaxSetup()中安装默认完整处理程序,但这很狡猾,因为如果Ajax调用覆盖完整的处理程序,则不会调用默认值.另外,我只能在文档首先加载文档后才注入(到那时某些AJAX调用可能已经在运行).我已经用上呼叫测试了此注射,它适用于在命令上发射的Ajax调用(在注射默认处理程序之后)不提供自己的完整处理程序.

我正在寻找两件事:首先:一种通用的方法,可以将Ajax呼叫的完整处理程序连接起来,其次:等待WebEngine完成所有Ajax呼叫并之后通知我的方法.

.

推荐答案

说明

我也遇到了这个问题,并通过提供自己的sun.net.www.protocol.http.HttpURLConnection实现来解决它,我用来处理任何AJAX请求.我的类方便地称为AjaxHttpURLConnection,将其挂在getInputStream()函数中,但不会返回其原始输入流.相反,我将PipedInputStream的实例返回到WebEngine.然后,我读取来自原始输入流的所有数据,然后将其传递到我的管道流.
这样,我获得了2个好处:

  1. 我知道何时收到了最后一个字节,因此已完全处理了Ajax请求.
  2. 我什至可以抓住所有传入的数据并已经使用了(如果我愿意的话).

示例

首先,您必须告诉Java使用您的URLConnection实现而不是默认一个.为此,您必须为其提供自己的URLStreamHandlerFactory版本.您可以在这里找到许多线程(例如 this )或通过Google通过Google.话题.为了设置您的工厂实例,请在main方法的早期将以下内容放置.这就是我的样子.

import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;

public class MyApplication extends Application {

    // ...

    public static void main(String[] args) {
        URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
            public URLStreamHandler createURLStreamHandler(String protocol) {
                if ("http".equals(protocol)) {
                    return new MyUrlConnectionHandler();    
                }
                return null; // Let the default handlers deal with whatever comes here (e.g. https, jar, ...)
            }
        });
        launch(args);
    }
}

第二,我们必须提出自己的Handler,该>告诉程序何时使用URLConnection.

import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;

import sun.net.www.protocol.http.Handler;
import sun.net.www.protocol.http.HttpURLConnection;

public class MyUrlConnectionHandler extends Handler {

    @Override
    protected URLConnection openConnection(URL url, Proxy proxy) throws IOException {

        if (url.toString().contains("ajax=1")) {
            return new AjaxHttpURLConnection(url, proxy, this);
        }

        // Return a default HttpURLConnection instance.
        return new HttpURLConnection(url, proxy);
    }
}

最后但并非最不重要的一点,这是AjaxHttpURLConnection.

import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.Proxy;
import java.net.URL;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.io.IOUtils;

import sun.net.www.protocol.http.Handler;
import sun.net.www.protocol.http.HttpURLConnection;

public class AjaxHttpURLConnection extends HttpURLConnection {

    private PipedInputStream pipedIn;
    private ReentrantLock lock;

    protected AjaxHttpURLConnection(URL url, Proxy proxy, Handler handler) {
        super(url, proxy, handler);
        this.pipedIn = null;
        this.lock = new ReentrantLock(true);
    }

    @Override
    public InputStream getInputStream() throws IOException {

        lock.lock();
        try {

            // Do we have to set up our own input stream?
            if (pipedIn == null) {

                PipedOutputStream pipedOut = new PipedOutputStream();
                pipedIn = new PipedInputStream(pipedOut);

                InputStream in = super.getInputStream();
                /*
                 * Careful here! for some reason, the getInputStream method seems
                 * to be calling itself (no idea why). Therefore, if we haven't set
                 * pipedIn before calling super.getInputStream(), we will run into
                 * a loop or into EOFExceptions!
                 */

                // TODO: timeout?
                new Thread(new Runnable() {
                    public void run() {
                        try {

                            // Pass the original data on to the browser.
                            byte[] data = IOUtils.toByteArray(in);
                            pipedOut.write(data);
                            pipedOut.flush();
                            pipedOut.close();

                            // Do something with the data? Decompress it if it was
                            // gzipped, for example.

                            // Signal that the browser has finished.

                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        } finally {
            lock.unlock();
        }
        return pipedIn;
    }
}

进一步的考虑

  • 如果您使用的是多个WebEngine对象,则说出哪个实际打开URLConnection,从而可能很难
  • 您可能已经注意到我只能使用HTTP连接.我尚未测试我的方法在多大程度上可以转移到HTTPS等.(这里不是专家:O).
  • 如您所见,我唯一知道何时实际使用我的AjaxHttpURLConnection的方法是相应的URL包含ajax=1.就我而言,这足够了.但是,由于我对HTML和HTTP不太好,但是我不知道WebEngine是否可以以任何不同的方式提出AJAX请求(例如,标题字段?).如有疑问,您可以随时仅返回我们修改的URL连接的实例,但这当然意味着一些开销.
  • 正如一开始所述,如果您愿意,您可以立即从输入流检索到数据后立即使用.您可以获取WebEngine以类似方式发送的请求数据.只需包装getOutputStream()函数,然后放置另一个中间流以获取要发送的所有内容,然后将其传递到原始输出流.

其他推荐答案

这是 @dadoosh答案的延伸…

为https做这件事是委派的噩梦,因为HttpsURLConnection(Impl)不能像HttpURLConnection

那样实例化

import sun.net.www.protocol.https.Handler;

public class MyStreamHandler extends Handler {

    @Override
    protected URLConnection openConnection(URL url) throws IOException {
        URLConnection connection = super.openConnection(url);
        if (url.toString().contains("ajax=1")) {
            return new MyConnection((HttpsURLConnection) connection);
        } else {
            return connection;
        }
    }
}

所以我得到了本来可以返回的连接,并在必要时将其交给MyConnection,以便它可以委派所有呼叫并修改getInputStream()方法.

顺便说一句,我找到了另一个用于检测AJAX请求末尾的解决方案.我只是等待close()被调用:

@Override
public synchronized InputStream getInputStream() throws IOException {
    if (cachedInputStream != null) {
        return cachedInputStream;
    }

    System.out.println("Open " + getURL());
    InputStream inputStream = delegate.getInputStream();

    cachedInputStream = new FilterInputStream(inputStream) {
        @Override
        public void close() throws IOException {
            super.close();
            // Signal that the browser has finished.
        }
    };

    return cachedInputStream;
}

以上所述是小编给大家介绍的JavaFX WebEngine等待ajax完成,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对77isp云服务器技术网的支持!

原文链接:https://77isp.com/post/34141.html

=========================================

https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。