55

Is there a way to view the http response headers in an Activity once a web page has been loaded in a WebView? Seems like this should be possible, but I can't find any methods that expose the headers.

tronman
  • 9,862
  • 10
  • 46
  • 61
  • 1
    No. What exactly are you trying to do? If you want to look at the request/response you will have to make the request using HttpClient. – Robby Pond Jun 28 '10 at 17:13
  • I'm trying to read an http response header from the page loaded in my WebView. I had a feeling I would have to use HttpClient and then send the response back to the WebView. More work than should be required to read a header response... – tronman Jun 28 '10 at 17:23

6 Answers6

44

Neither WebView nor WebViewClient provide methods to do that, Though, you can try to implement that manually. You can do something like this:

private WebView webview;
public void onCreate(Bundle icicle){
    // bla bla bla

    // here you initialize your webview
    webview = new WebView(this);
    webview.setWebViewClient(new YourWebClient());
}

// this will be the webclient that will manage the webview
private class YourWebClient extends WebViewClient{

    // you want to catch when an URL is going to be loaded
    public boolean  shouldOverrideUrlLoading  (WebView  view, String  urlConection){
        // here you will use the url to access the headers.
        // in this case, the Content-Length one
        URL url;
        URLConnection conexion;
        try {
            url = new URL(urlConection);
            conexion = url.openConnection();
            conexion.setConnectTimeout(3000);
            conexion.connect();
            // get the size of the file which is in the header of the request
            int size = conexion.getContentLength();
        }


        // and here, if you want, you can load the page normally
        String htmlContent = "";
        HttpGet httpGet = new HttpGet(urlConection);
        // this receives the response
        HttpResponse response;
        try {
            response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() == 200) {
                // la conexion fue establecida, obtener el contenido
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    InputStream inputStream = entity.getContent();
                    htmlContent = convertToString(inputStream);
                }
            }
         } catch (Exception e) {}

         webview.loadData(htmlContent, "text/html", "utf-8");
         return true;
    }

    public String convertToString(InputStream inputStream){
        StringBuffer string = new StringBuffer();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        String line;
        try {
            while ((line = reader.readLine()) != null) {
                string.append(linea + "\n");
            }
        } catch (IOException e) {}
        return string.toString();
    }
}

I can't test it right now, but that's basically what you could do (it's very crazy though :).

Cristian
  • 198,401
  • 62
  • 356
  • 264
  • 2
    The only problem with this method is that shouldOverrideUrlLoading() is not called when calling the WebView's loadUrl(). This is a big problem for me. – tronman Jun 28 '10 at 19:00
  • 1
    Well... in that case you just have to override the `loadUrl()` method ;) – Cristian Jun 28 '10 at 19:21
  • 12
    Since its been 4 years since this question was asked. I wondered if there were any fully prepared solutions? Or is this still the best way to go? If so where does the httpClient come from? – John Shelley Oct 03 '14 at 19:55
  • 1
    @JohnShelley did you find out where HttpClient comes from? I'm trying to solve this as well but I don't know where they're getting HttpResponse from. – b.lyte Mar 18 '16 at 18:47
  • @clu sorry I did not, that was about 1.5 years ago so not sure. – John Shelley Mar 18 '16 at 20:35
  • @clu You just create one (although it's deprecated in Android 6) `DefaultHttpClient httpClient = new DefaultHttpClient();` – Unglückspilz Mar 30 '16 at 16:37
  • 3
    I don't think this will work very well since shouldOverrideUrlLoading only seems to be called for GET requests. What if you wanted to intercept headers on a POST request? shouldInterceptRequest is the more proper way to do it but it requires lollipop. – craigrs84 Dec 02 '16 at 18:10
6

inspired by Cristian answer I needed to intercept AJAX calls webview is doing, where I needed to intercept response headers to get some information (cart item count in e-commerce app), which I needed to leverage in app. As the app is using okhttp I've ended up doing this and it's working:

        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
            Log.i(TAG,"shouldInterceptRequest path:"+request.getUrl().getPath());
            WebResourceResponse returnResponse = null;
            if (request.getUrl().getPath().startsWith("/cart")) { // only interested in /cart requests
                returnResponse = super.shouldInterceptRequest(view, request);
                Log.i(TAG,"cart AJAX call - doing okRequest");
                Request okRequest = new Request.Builder()
                        .url(request.getUrl().toString())
                        .post(null)
                        .build();
                try {
                    Response okResponse = app.getOkHttpClient().newCall(okRequest).execute();
                    if (okResponse!=null) {
                        int statusCode = okResponse.code();
                        String encoding = "UTF-8";
                        String mimeType = "application/json";
                        String reasonPhrase = "OK";
                        Map<String,String> responseHeaders = new HashMap<String,String>();
                        if (okResponse.headers()!=null) {
                            if (okResponse.headers().size()>0) {
                                for (int i = 0; i < okResponse.headers().size(); i++) {
                                    String key = okResponse.headers().name(i);
                                    String value = okResponse.headers().value(i);
                                    responseHeaders.put(key, value);
                                    if (key.toLowerCase().contains("x-cart-itemcount")) {
                                        Log.i(TAG,"setting cart item count");
                                        app.setCartItemsCount(Integer.parseInt(value));
                                    }
                                }
                            }
                        }
                        InputStream data = new ByteArrayInputStream(okResponse.body().string().getBytes(StandardCharsets.UTF_8));
                        Log.i(TAG, "okResponse code:" + okResponse.code());
                        returnResponse = new WebResourceResponse(mimeType,encoding,statusCode,reasonPhrase,responseHeaders,data);
                    } else {
                        Log.w(TAG,"okResponse fail");
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return returnResponse;
        }

I hope this may be helpful to others and if somebody has a suggestions for improvement I would be grateful. Unfortunately it's compatible only with LOLLIPOP and higher as from this version you can access/return headers using WebResourceRequest, which was needed for my case.

Community
  • 1
  • 1
ursimon
  • 151
  • 1
  • 6
  • Is there no alternative to WebResourceRequest for older android versions? Is it not possible to capture Ajax requests in older android versions? – Sohaib Mar 31 '15 at 10:19
  • alternatively you can use http://developer.android.com/guide/webapps/webview.html#BindingJavaScript if you can change what website/webapp is doing – ursimon May 22 '15 at 13:03
  • I'm trying to do this on older versions of Android as well. Any way to do this? – b.lyte Mar 18 '16 at 18:47
  • Ursimon, I tried to achieve the same thing, but it seems the call to super returns null. Source code looks pretty straight forward btw... Could you please tell me how did you made it work? @clu Could you please also help me? – Zsolt Boldizsar Apr 05 '16 at 13:42
1

You should be able to control all your headers by skipping loadUrl and writing your own loadPage using Java's HttpURLConnection. Then view the headers, do your thing, and use the webview's loadData to display the response.

R Earle Harris
  • 985
  • 9
  • 17
1

There is an alternative solution if you're targeting at least Kit-Kat, even though this wouldn't show the headers in the Activity but rather in Chrome. You can simply follow this short guide on how to remotely debug Webviews.

The 2 key points are, first, to enable WebView debugging in you app

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    WebView.setWebContentsDebuggingEnabled(true);
}

And then open chrome://inspect in a Chrome tab in a computer. Once you connect your phone via USB to the computer, you will see your app's WebView in the list of debuggable devices

-1

you can use OkHttp:

private fun handleRequestViaOkHttp(url: String) {

            var httpClient = OkHttpClient()

            thread {
                try {
                    val request = Request.Builder().url(url).build()
                    print("Request: $request")
                    val response = httpClient.newCall(request).execute()
                    println("Response: " + response.headers().toString())
                } catch (e: Exception) {}
            }
        }

you should call it inside this method:

override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {

                handleRequestViaOkHttp(webViewUrl.value.toString())
                return super.shouldInterceptRequest(view, request)
        }
Diogo Sequeira
  • 126
  • 1
  • 9
-3

As the accepted answer will only work with HttpGet, here is a trick thet currently I'm using (at this time it seems to work)

In onPageFinished handler, if there is an error, the title of the page will be like "ERROR_NUM - ERROR_DESCRIPTION", like "500 - Internal Server Error", so all I do is to get title from webview in the function, and then check the title.

view.getTitle()

Mehran SH
  • 1
  • 4