Можно ли получить доступ к данным формы HTML POSTed через WebView?
Я начал писать приложение, которое предоставляет пользователю HTML-форму через WebView. Поскольку форма не находится под моим контролем, заполненные данные могут быть отправлены как запрос GET или POST. Моему приложению требуется захватить перенесенные данные формы, то есть получить информацию о том, что было введено в поля формы.
Использование адекватного обратного вызова из WebViewClient, такого как onPageLoaded()
, легко получить данные формы из запроса GET. Тем не менее, я не могу найти какой-либо подходящий метод, чтобы разрешить то же самое для данных POST, то есть иметь возможность доступа к телу сообщения HTTP POST, содержащему данные формы. Я пропускаю соответствующий обратный вызов здесь или просто нет способа достичь указанной цели с помощью данного API (даже самого последнего уровня 8)?
Предполагая, что это невозможно, я подумал о переопределении и расширении частей android.webkit, чтобы ввести новый обработчик обратного вызова, который каким-то образом проходит через тело POST. Таким образом, мое приложение может быть поставлено с настроенным браузером /WebViewClient, который выполняет желаемую функцию. Тем не менее, я не смог найти хорошее место для начала в коде и был бы рад любым подсказкам в этом отношении (в случае, если подход выглядит многообещающим).
Заранее спасибо!
2 ответа
Как указано в моем собственном комментарии к исходному вопросу, подход с использованием JavaScript-инъекций работает. По сути, вам нужно добавить какой-то фрагмент кода JavaScript в событие onsubmit DOM, проанализировать поля формы и вернуть результат обратно в 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 будет анализировать данные формы и передавать их обратно в землю Java через JavaScriptInterface
объект.
Чтобы разобрать поля формы, код Javascript добавляет форму onsubmit
и кнопка onclick
обработчики. Первый может обрабатывать отправку канонической формы через обычную кнопку отправки, в то время как последний имеет дело с пользовательскими кнопками отправки, то есть кнопками, которые выполняют некоторую дополнительную магию Javascript перед вызовом. form.submit()
,
Помните, что код Javascript может быть не идеальным: могут быть другие способы отправки формы, которые мой внедренный код может не уловить. Тем не менее, я убежден, что введенный код можно обновить, чтобы справиться с такими возможностями.
Приведенный ответ дает ошибку, поэтому я решил сделать более простую реализацию, в которой также был бы хорошо структурированный 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);
Тогда ваша активность / фрагмент должен выглядеть так:
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();
}