【问题标题】:Access the http response headers in a WebView?访问 WebView 中的 http 响应标头?
【发布时间】:2010-06-28 16:52:27
【问题描述】:

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

【问题讨论】:

  • 没有。你到底想做什么?如果您想查看请求/响应,则必须使用 HttpClient 发出请求。
  • 我正在尝试从我的 WebView 中加载的页面读取 http 响应标头。我有一种感觉,我必须使用 HttpClient,然后将响应发送回 WebView。比读取标头响应所需的工作更多...

标签: android


【解决方案1】:

WebViewWebViewClient 都没有提供执行此操作的方法,但您可以尝试手动实现。你可以这样做:

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();
    }
}

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

【讨论】:

  • 这个方法唯一的问题是调用WebView的loadUrl()时没有调用shouldOverrideUrlLoading()。这对我来说是个大问题。
  • 嗯...在这种情况下,您只需要覆盖loadUrl() 方法;)
  • 自从提出这个问题已经 4 年了。我想知道是否有任何完全准备好的解决方案?或者这仍然是最好的方法吗?如果是,httpClient 是从哪里来的?
  • @JohnShelley 你发现 HttpClient 来自哪里了吗?我也在尝试解决这个问题,但我不知道他们从哪里获得 HttpResponse。
  • 我认为这不会很好,因为 shouldOverrideUrlLoading 似乎只为 GET 请求调用。如果您想拦截 POST 请求的标头怎么办? shouldInterceptRequest 是更合适的方法,但它需要棒棒糖。
【解决方案2】:

灵感来自Cristiananswer 我需要拦截 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;
        }

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

【讨论】:

  • 对于旧的 android 版本是否没有 WebResourceRequest 的替代品?是不是不能在旧版本的android中捕获Ajax请求?
  • 如果您可以更改网站/webapp 正在执行的操作,您也可以使用developer.android.com/guide/webapps/…
  • 我也在尝试在旧版本的 Android 上执行此操作。有什么办法吗?
  • Ursimon,我试图实现同样的目标,但似乎对 super 的调用返回 null。顺便说一句,源代码看起来很简单......你能告诉我你是如何让它工作的吗? @clu 你能帮帮我吗?
【解决方案3】:

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

【讨论】:

  • 您能详细说明您的答案吗?自从 4 年以来,我希望有更好的方法来解决它。
  • @MW 显然不是。也许在未来 4 年
  • 再等 4 年,他们会弃用 android 并在 Flutter 中引入 api
【解决方案4】:

如果您的目标至少是 Kit-Kat,还有一个替代解决方案,即使这不会在 Activity 中显示标题,而是在 Chrome 中显示。您可以简单地按照这个简短的guide 了解如何远程调试 Webview。

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

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

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

【讨论】:

    【解决方案5】:

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

    你应该在这个方法中调用它:

    override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
    
                    handleRequestViaOkHttp(webViewUrl.value.toString())
                    return super.shouldInterceptRequest(view, request)
            }
    

    【讨论】:

    • 这会执行两次相同的 http 调用
    【解决方案6】:

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

    在 onPageFinished 处理程序中,如果有错误,页面的标题会像“ERROR_NUM - ERROR_DESCRIPTION”,像“500 - Internal Server Error”,所以我所做的就是从函数中的webview获取标题,然后检查标题。

    view.getTitle()

    【讨论】:

    • 这与获取响应头没有任何关系。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-14
    • 2012-02-24
    • 2015-07-16
    • 2016-02-13
    • 1970-01-01
    相关资源
    最近更新 更多