【问题标题】:is it possible to access HTML form data POSTed through a WebView?是否可以访问通过 WebView 发布的 HTML 表单数据?
【发布时间】:2011-04-06 12:59:38
【问题描述】:

我已经开始编写一个通过 WebView 为用户提供 HTML 表单的应用程序。由于表单不在我的控制之下,填写的数据可以作为 GET 或 POST 请求发送。我的应用程序需要捕获传输的表单数据,即获取输入到表单字段中的内容。

使用来自 WebViewClient 的适当回调(例如 onPageLoaded()),很容易从 GET 请求中捕获表单数据。但是,我找不到任何合适的方法来允许对 POST 数据使用相同的方法,即能够访问包含表单数据的 HTTP POST 消息正文。我是否在这里遗漏了相关的回调,或者根本无法使用给定的 API(即使是最新的 8 级)完成指定的目标?

假设这是不可能的,我考虑覆盖和扩展 android.webkit 的部分,以便引入一个新的回调钩子,它以某种方式传递给 POST 主体。这样,我的应用程序可以附带一个定制的浏览器/WebViewClient 来满足所需的功能。但是,我在代码中找不到任何好的起点,我很乐意在这方面提供任何提示(以防该方法看起来很有希望)。

提前致谢!

【问题讨论】:

  • 其实我自己可能已经找到了一种方法:应该可以在用户提交表单时注入一些解析表单字段的JavaScript。提供的 Javascript-to-Java 接口应该能够将填充的数据传输回 Java 空间以进行进一步处理。将尝试这种方法并报告回来。当然,仍然欢迎任何进一步的反馈!

标签: android


【解决方案1】:

正如我自己对原始问题的评论所述,JavaScript 注入方法有效。基本上,您需要做的是向 DOM onsubmit 事件添加一些 JavaScript 代码,让它解析表单的字段,并将结果返回给 Java 注册的函数。

代码示例:

public class MyBrowser extends Activity {
    private final String jsInjectCode = 
        "function parseForm(event) {" +
        "    var form = this;" +
        "    // make sure form points to the surrounding form object if a custom button was used" +
        "    if (this.tagName.toLowerCase() != 'form')" +
        "        form = this.form;" +    
        "    var data = '';" +
        "    if (!form.method)  form.method = 'get';" +
        "    data += 'method=' + form.method;" +
        "    data += '&action=' + form.action;" +        
        "    var inputs = document.forms[0].getElementsByTagName('input');" +
        "    for (var i = 0; i < inputs.length; i++) {" +
        "         var field = inputs[i];" +
        "         if (field.type != 'submit' && field.type != 'reset' && field.type != 'button')" +
        "             data += '&' + field.name + '=' + field.value;" +
        "    }" +
        "    HTMLOUT.processFormData(data);" +
        "}" +
        "" +
        "for (var form_idx = 0; form_idx < document.forms.length; ++form_idx)" +
        "    document.forms[form_idx].addEventListener('submit', parseForm, false);" +    
        "var inputs = document.getElementsByTagName('input');" +
        "for (var i = 0; i < inputs.length; i++) {" +
        "    if (inputs[i].getAttribute('type') == 'button')" +
        "        inputs[i].addEventListener('click', parseForm, false);" +
        "}" +
        "";

    class JavaScriptInterface {
        @JavascriptInterface
        public void processFormData(String formData) {
            //added annotation for API > 17 to make it work
            <do whatever you need to do with the form data>
        }
    }

    onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.browser);
        WebView browser = (WebView)findViewById(R.id.browser_window);
        browser.getSettings().setJavaScriptEnabled(true);
        browser.addJavascriptInterface(new JavaScriptInterface(), "HTMLOUT");
        browser.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            view.loadUrl("javascript:(function() { " + 
                      MyBrowser.jsInjectCode + "})()");
        }
}

通俗地说,这是在页面完成加载时注入自定义 JavaScript 代码(作为onsubmit 处理程序)。提交表单后,Javascript 将解析表单数据并通过 JavaScriptInterface 对象将其传递回 Java 平台。

为了解析表单字段,Javascript 代码添加了表单onsubmit 和按钮onclick 处理程序。前者可以通过常规提交按钮处理规范的表单提交,而后者处理自定义提交按钮,即在调用 form.submit() 之前执行一些额外的 Javascript 魔法的按钮。

请注意,Javascript 代码可能并不完美:可能有其他方法可以提交我注入的代码可能无法捕获的表单。但是,我确信注入的代码可以更新以应对这种可能性。

【讨论】:

  • window.HTMLOUT.processFormData(data); 对我不起作用,但 HTMLOUT.processFormData(data); 对我起作用。
  • 我在 Android 4.4.4 "chromium: [INFO:CONSOLE(1)] "Uncaught SyntaxError: Unexpected end of input", source: (1)" 有什么想法吗?
  • 从表单中检索到的值在哪里?
  • @headsvk 删除注释行。
【解决方案2】:

提供的答案给出了错误,所以我决定做一个更简单的实现,它还具有结构良好的 JavaScript(意味着 JS 在文件中):

在您的资产文件夹中创建一个名为 inject.js 的文件,其中包含以下代码:

document.getElementsByTagName('form')[0].onsubmit = function () {
    var objPWD, objAccount, objSave;
    var str = '';
    var inputs = document.getElementsByTagName('input');
    for (var i = 0; i < inputs.length; i++) {
        if (inputs[i].name.toLowerCase() === 'username') {
            objAccount = inputs[i];
        } else if (inputs[i].name.toLowerCase() === 'password') {
            objPWD = inputs[i];
        } else if (inputs[i].name.toLowerCase() === 'rememberlogin') {
            objSave = inputs[i];
        }
    }
    if(objAccount != null) {
        str += objAccount.value;
    }
    if(objPWD != null) {
        str += ' , ' + objPWD.value;
    }
    if(objSave != null) {
        str += ' , ' + objSave.value;
    }
    window.AndroidInterface.processHTML(str);
    return true;
};

这是我们将用于注入的 javascript 代码,您可以根据需要切换 if 语句并使用类型或名称。Android 的回调是这一行:window.AndroidInterface.processHTML(str);

那么你的 Activity/fragment 应该是这样的:

public class MainActivity extends ActionBarActivity {
    class JavaScriptInterface {
        @JavascriptInterface
        public void processHTML(String formData) {
            Log.d("AWESOME_TAG", "form data: " + formData);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        WebView webView = new WebView(this);
        this.setContentView(webView);

        // enable javascript
        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webView.addJavascriptInterface(new JavaScriptInterface(), "AndroidInterface");

        // catch events
        webView.setWebViewClient(new WebViewClient(){
            @Override
            public void onPageFinished(WebView view, String url) {
                try {
                    view.loadUrl("javascript:" + buildInjection());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        webView.loadUrl("http://someurl.com");
    }

    private String buildInjection() throws IOException {
        StringBuilder buf = new StringBuilder();
        InputStream inject = getAssets().open("inject.js");// file from assets
        BufferedReader in = new BufferedReader(new InputStreamReader(inject, "UTF-8"));
        String str;
        while ((str = in.readLine()) != null) {
            buf.append(str);
        }
        in.close();

        return buf.toString();
    }

【讨论】:

  • 输入框的值是从哪里得到的
猜你喜欢
  • 2023-02-18
  • 1970-01-01
  • 2015-09-26
  • 1970-01-01
  • 1970-01-01
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多