You need to enable JavaScript to run this app.
导航

WebView

最近更新时间2024.03.29 17:41:10

首次发布时间2023.03.22 20:49:16

方案描述

如果您的 app 使用了 WebView,您可以通过以下方式拦截和转发请求:

  1. 使用 shouldInterceptRequest 回调拦截请求。
  2. shouldInterceptRequest 回调中,使用 OkHttp 发送请求。基于 OkHttp 请求结果构造原生 WebResourceResponse 响应。
  3. 使用 WebView.loadUrl 加载请求。

前提条件

警告

对于没有在控制台添加的域名,HTTPDNS 服务端的解析会失败,您只能获得 Local DNS 服务器的解析结果。参见 添加需要解析的域名了解如何添加域名。

实现步骤

  1. 使用 shouldInterceptRequest 拦截请求。

    注意

    • shouldInterceptRequest 不能拦截带有 body 的请求。如果您的请求带有 body,body 会丢失。
    • 您的 app 的 Android API 等级需要大于或等于 21。如果 API 等级小于 21,shouldInterceptRequest 无法正常拦截请求。
    String targetUrl = "https://www.bytedance.com";
    WebView webView = findViewById(R.id.webviewid);
    webView.setWebViewClient(new WebViewClient() {
                // API >= 21
                @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
                @Override
                public WebResourceResponse shouldInterceptRequest(WebView view,
                                                                WebResourceRequest request) {
                    String scheme = request.getUrl().getScheme().trim();
                    String method = request.getMethod();
                    Map<String, String> headerFields = request.getRequestHeaders();
                    String url = request.getUrl().toString();
                    Log.d(TAG, "intercept request for url: " + url);
                    
                    ...
                
                    }
                    return super.shouldInterceptRequest(view, request);
                }
    
                @Override
                public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                    // API < 21 时,shouldInterceptRequest 无法正常拦截请求
                    return super.shouldInterceptRequest(view, url);
                }
            );
    
  2. shouldInterceptRequest 回调中,使用 OkHttp 转发请求。基于 OkHttp 请求结果构造原生 WebResourceResponse 响应。

    // 使用 OkHttp 转发请求
    // 无法拦截 body,拦截方案只能正常处理不带 body 的请求;
    if (method.equalsIgnoreCase("get")) {
        try {
            OkHttpClient okHttpClient = new OkHttpClient.Builder().dns(new Dns() {
                @Override
                public List<InetAddress> lookup(String hostname) throws UnknownHostException {
                    long beforeResolve = System.currentTimeMillis();
                    // 获取解析结果
                    DnsResult dnsResult = HttpDns.getService().getHttpDnsResultForHostSyncBlock(hostname);
                    if (dnsResult != null && (!dnsResult.ipv4List.isEmpty() || !dnsResult.ipv6List.isEmpty())) {
                        StringBuilder dnsResultString = new StringBuilder("host : " + dnsResult.host + ", " +
                                "getHttpDnsResultForHostSyncBlock cost : " + (System.currentTimeMillis() - beforeResolve) + ", " +
                                "dns source : " + dnsResult.source + ", " +
                                "ipv4 list : " + dnsResult.ipv4List + ", " +
                                "ipv6 list : " + dnsResult.ipv6List + ", " +
                                "dns result ttl: " + dnsResult.ttl + ", " +
                                "dns request cost: " + dnsResult.rtt + ", " +
                                "client ip: " + dnsResult.cip + "\n");
                        List<DnsTaskInfo> taskInfos = dnsResult.taskInfoList;
                        for (DnsTaskInfo taskInfo : taskInfos) {
                            dnsResultString.append(taskInfo.toJson()).append("\n");
                        }
                        Log.e(TAG, "dns result " + dnsResultString);
                        List<InetAddress> inetAddresses = new ArrayList<>();
                        Iterator<String> itV6 = dnsResult.ipv6List.iterator();
                        Iterator<String> itV4 = dnsResult.ipv4List.iterator();
                        while (itV6.hasNext() || itV4.hasNext()) {
                            if (itV6.hasNext()) {
                                            inetAddresses.add(InetAddress.getByName(itV6.next()));
                            }
                            if (itV4.hasNext()) {
                                            inetAddresses.add(InetAddress.getByName(itV4.next()));
                            }
                        }
                        return inetAddresses;
                    } else {
                        // 如果 HTTPDNS SDK 通过 HTTPDNS 服务端和 Local DNS 服务器的解析都失败了,
                        // 您可以使用 OkHttp 提供的方法再次向 Local DNS 服务器发起查询请求。
                        return Dns.SYSTEM.lookup(hostname);
                    }
                }
            }).build();
    
            // 使用 OkHttp 创建请求
            okhttp3.Request.Builder okRequestBuilder = new okhttp3.Request.Builder().url(url);
            if (headerFields != null) {
                for (Map.Entry<String, String> field : headerFields.entrySet()) {
                    okRequestBuilder.header(field.getKey(), field.getValue());
                }
            }
            Request okRequest = okRequestBuilder.build();
            Response okResponse = okHttpClient.newCall(okRequest).execute();
            String contentType = okResponse.header("Content-Type");
            String mime = WebViewUtlis.getMime(contentType);
            String charset = WebViewUtlis.getCharset(contentType);
            int statusCode = okResponse.code();
            String responseMessage = okResponse.message();
            Log.d(TAG, "response code is " + statusCode + ", for url: " + url);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
  3. 使用 WebView.loadUrl 加载请求。

    webView.loadUrl(targetUrl);
    

相关信息

如果您需要了解 HTTPDNS Android SDK 的详细信息,参见 Android SDK 参考