Reputation: 421
I'm developping an Android application filtering the requests (with a white list) and using a custom SSLSocketFactory
. For this, I've developed a custom WebViewClient
and I have overridden the shouldInterceptRequest
method. I can filter and use my SocketFactory
with the GET requests but I can't intercept the POST requests.
So, is there a way to intercept the POST requests in a WebView
?
Here is the code of the shouldInterceptRequest method :
public final WebResourceResponse shouldInterceptRequest(WebView view, String urlStr) {
URI uri = URI.create(urlStr);
String scheme = uri.getScheme();
// If scheme not http(s), let the default webview manage it
if(!"http".equals(scheme) && !"https".equals(scheme)) {
return null;
}
URL url = uri.toURL();
if(doCancelRequest(url)) {
// Empty response
Log.d(TAG, "URL filtered: " + url);
return new WebResourceResponse("text/plain", "UTF-8", new EmptyInputStream());
} else {
Log.d(TAG, "URL: " + url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("User-Agent", mSettings.getUserAgentString());
// Configure connections
configureConnection(conn);
String mimeType = conn.getContentType();
String encoding = conn.getContentEncoding();
if(mimeType != null && mimeType.contains(CONTENT_TYPE_SPLIT)) {
String[] split = mimeType.split(CONTENT_TYPE_SPLIT);
mimeType = split[0];
Matcher matcher = CONTENT_TYPE_PATTERN.matcher(split[1]);
if(matcher.find()) {
encoding = matcher.group(1);
}
}
InputStream is = conn.getInputStream();
return new WebResourceResponse(mimeType, encoding, is);
}
}
Upvotes: 42
Views: 45440
Reputation: 1632
I have created a library that aims to capture all data of all HTTP requests sent from Android WebViews.
Using this library, you can easily implement sending POST
, or any other requests. Here's your code adapted to work with the library:
@Nullable
@Override
public final WebResourceResponse shouldInterceptRequest(WebView view, WebViewRequest request) {
URI uri = URI.create(request.getUrl());
String scheme = uri.getScheme();
// If scheme not http(s), let the default webview manage it
if(!"http".equals(scheme) && !"https".equals(scheme)) {
return null;
}
URL url = uri.toURL();
if(doCancelRequest(url)) {
// Empty response
Log.d(TAG, "URL filtered: " + url);
return new WebResourceResponse("text/plain", "UTF-8", new EmptyInputStream());
} else {
Log.d(TAG, "URL: " + url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// Set request method
conn.setRequestMethod(request.getMethod());
// Set request headers
for (Map.Entry<String, String> header : request.getHeaders().entrySet()) {
conn.setRequestProperty(header.getKey(), header.getValue());
}
// Set request body, if it's present
if (!request.getBody().isEmpty()) {
OutputStream os = conn.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
osw.write(request.getBody());
osw.flush();
osw.close();
os.close(); //don't forget to close the OutputStream
}
conn.connect();
String mimeType = conn.getContentType();
String encoding = conn.getContentEncoding();
if(mimeType != null && mimeType.contains(CONTENT_TYPE_SPLIT)) {
String[] split = mimeType.split(CONTENT_TYPE_SPLIT);
mimeType = split[0];
Matcher matcher = CONTENT_TYPE_PATTERN.matcher(split[1]);
if(matcher.find()) {
encoding = matcher.group(1);
}
}
InputStream is = conn.getInputStream();
return new WebResourceResponse(mimeType, encoding, is);
}
}
Upvotes: 5
Reputation: 1
There's a simpler solution: send the parameters by GET in the ajax call and transform them to POST in shouldInterceptRequest
Upvotes: -1
Reputation: 151
The easiest way I found to do this is to just use the JQuery Ajax Event Handlers with a Javascript Interface.
You have to setup the Javascript Interface, but once you do just have it match the signature of the handler you're interested in.
To get the payload, just find the payload variable name put it in the interface.
Here's the Javascript:
$( document ).ajaxSend(function( event, request, settings ) {
var eventJSON= JSON.stringify(event);
var requestJSON = JSON.stringify(request);
var settingsJSON = JSON.stringify(settings);
var payloadJSON = JSON.stringify(payload);
myJSInterface.passData(eventJSON,requestJSON,settingsJSON,payloadJSON);
});
Here's the Kotlin class
class yourJSInterface() {
lateinit var event: String
lateinit var request: String
lateinit var settings: String
lateinit var payload: String
@JavascriptInterface
fun passData(eventJSON:String, responseJSON:String, settingsJSON:String,payloadJSON:String){
event = eventJSON
response= responseJSON
settings= settingsJSON
payload= payloadJSON
}
}
The registration in the onPageStarted override for WebViewClient
webView.addJavascriptInterface(yourJSInterface,"myJSInterface")
Finally the JS injection into the WebView in the OnPageFinished override
webView.evaluateJavascript("javascript:" + getString(R.string.js_from_above),null)
I registered the interface in onPageStarted because otherwise, the javascript file in onPageFinished won't recognize your interface.
Upvotes: 0
Reputation: 3356
I was facing the same issue a few days ago.
So I built a library that solves it:
https://github.com/KonstantinSchubert/request_data_webviewclient
It is a WebViewClient with a custom WebResourceRequest that contains the POST/PUT/... payload of XMLHttpRequest requests.
It only works for these though - not for forms and other kind of request sources.
The hack works, basically, by injecting a script into the HTML that intercepts XMLHttpRequest calls. It records the post/put/... content and sends it to an android.webkit.JavascriptInterface
. There, the request is stashed until the shouldInterceptRequest
method is called by Android ...
Upvotes: 9
Reputation: 1459
you can get input value before submit
https://github.com/henrychuangtw/WebView-Javascript-Inject
Step 1 : create a class which called by javascript
class MyJavaScriptInterface
{
@JavascriptInterface
public void processHTML(String html)
{
//called by javascript
}
}
Step 2 : register interface for javascript
webview1.getSettings().setJavaScriptEnabled(true);
webview1.addJavascriptInterface(new MyJavaScriptInterface(), "MYOBJECT");
Step 3 : inject javascript to page
webview1.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
StringBuilder sb = new StringBuilder();
sb.append("document.getElementsByTagName('form')[0].onsubmit = function () {");
sb.append("var objPWD, objAccount;var str = '';");
sb.append("var inputs = document.getElementsByTagName('input');");
sb.append("for (var i = 0; i < inputs.length; i++) {");
sb.append("if (inputs[i].type.toLowerCase() === 'password') {objPWD = inputs[i];}");
sb.append("else if (inputs[i].name.toLowerCase() === 'email') {objAccount = inputs[i];}");
sb.append("}");
sb.append("if (objAccount != null) {str += objAccount.value;}");
sb.append("if (objPWD != null) { str += ' , ' + objPWD.value;}");
sb.append("window.MYOBJECT.processHTML(str);");
sb.append("return true;");
sb.append("};");
view.loadUrl("javascript:" + sb.toString());
}
});
Upvotes: -1
Reputation: 485
I have one of my answers on above thread http://code.google.com/p/android/issues/detail?id=9122
Please see comment#31
Some of the caveats of my solution I see are:
Apart from that, I found this github repo which seems to be solving this problem in another hacky way. I looked into the code but didn't get time to implement and test it. But worth giving a try.
Upvotes: 0
Reputation: 4785
Use GET instead of POST.
Known issue: http://code.google.com/p/android/issues/detail?id=9122
Was answered here as well: Android - how to intercept a form POST in android WebViewClient on API level 4
Upvotes: -4