Android 访问 WebView 中的 http 响应标头?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3134389/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 08:51:17  来源:igfitidea点击:

Access the http response headers in a WebView?

android

提问by tronman

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.

在 WebView 中加载网页后,有没有办法在 Activity 中查看 http 响应标头?似乎这应该是可能的,但我找不到任何公开标题的方法。

采纳答案by Cristian

Neither WebViewnor WebViewClientprovide methods to do that, Though, you can try to implement that manually. You can do something like this:

既没有WebView也没有WebViewClient提供方法来做到这一点,虽然,你可以尝试手动实现。你可以这样做:

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 :).

我现在不能测试它,但这基本上是你可以做的(虽然这很疯狂:)。

回答by ursimon

inspired by CristiananswerI 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:

受到Cristian回答的启发,我需要拦截 webview 正在执行的 AJAX 调用,我需要拦截响应标头以获取一些信息(电子商务应用程序中的购物车项目计数),我需要在应用程序中利用这些信息。由于该应用程序正在使用 okhttp,我最终这样做了并且它正在工作

        @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.

我希望这可能对其他人有所帮助,如果有人有改进建议,我将不胜感激。不幸的是,它仅与 LOLLIPOP 和更高版本兼容,因为从这个版本开始,您可以使用WebResourceRequest访问/返回标头,这是我的案例所需要的。

回答by R Earle Harris

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.

您应该能够通过跳过 loadUrl 并使用 Java 的 HttpURLConnection 编写自己的 loadPage 来控制所有标头。然后查看标题,做你的事情,并使用 webview 的 loadData 来显示响应。

回答by Nicolás Carrasco

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 guideon how to remotely debug Webviews.

如果您的目标至少是 Kit-Kat,则有一个替代解决方案,即使这不会在 Activity 中显示标题,而是在 Chrome 中显示。你可以简单地遵循这个关于如何远程调试 Webviews 的简短指南

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

两个关键点是,首先,在您的应用程序中启用 WebView 调试

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

And then open chrome://inspectin 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

然后chrome://inspect在计算机的 Chrome 标签页中打开。通过 USB 将手机连接到计算机后,您将在可调试设备列表中看到您的应用程序的 WebView

回答by Diogo Sequeira

you can use OkHttp:

你可以使用 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)
        }

回答by Mehran SH

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)

由于接受的答案仅适用于 HttpGet,这是我目前正在使用的一个技巧(此时它似乎有效)

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.

在 onPageFinished 处理程序中,如果出现错误,页面标题将类似于“ERROR_NUM - ERROR_DESCRIPTION”,例如“500 - 内部服务器错误”,所以我所做的就是在函数中从 webview 获取标题,然后检查标题。

view.getTitle()

视图.getTitle()