0


Android webview拦截H5的接口请求并返回处理好的数据

Android webview拦截H5的接口请求并返回处理好的数据

Android 可以通过

  1. WebView

  1. shouldInterceptRequest

方法拦截到 H5 中的网络请求。这是一个

  1. WebViewClient

中的回调方法,允许开发者在 WebView 发起网络请求时对其进行处理和修改。

具体使用方法如下:

  1. 你需要创建一个自定义的 WebViewClient,并重写 shouldInterceptRequest 方法。
  2. 在该方法中,你可以拦截 WebView 发起的网络请求,并返回一个自定义的响应,或让请求继续。
  3. 该方法在 API 21 (Android 5.0) 及更高版本中引入了两个重载方法:- shouldInterceptRequest(WebView view, String url) (API 11)- shouldInterceptRequest(WebView view, WebResourceRequest request) (API 21)

以下是代码示例:

  1. // Kotlin 代码示例
  2. webView.webViewClient = object : WebViewClient() {
  3. // 对于 API 21 及更高版本
  4. override fun shouldInterceptRequest(
  5. view: WebView,
  6. request: WebResourceRequest
  7. ): WebResourceResponse? {
  8. val url = request.url.toString()
  9. // 这里你可以判断 URL,并根据需要拦截或修改请求
  10. if (url.contains("your_target_url")) {
  11. // 可以在这里做一些处理,例如替换请求或返回本地数据
  12. val inputStream = ... // 自定义输入流
  13. return WebResourceResponse("text/html", "UTF-8", inputStream)
  14. }
  15. // 不拦截,继续请求
  16. return super.shouldInterceptRequest(view, request)
  17. }
  18. // 对于 API 11 到 API 20 的设备
  19. override fun shouldInterceptRequest(
  20. view: WebView,
  21. url: String
  22. ): WebResourceResponse? {
  23. // 这里处理逻辑类似于上面的代码
  24. if (url.contains("your_target_url")) {
  25. // 自定义处理逻辑
  26. val inputStream = ...
  27. return WebResourceResponse("text/html", "UTF-8", inputStream)
  28. }
  29. return super.shouldInterceptRequest(view, url)
  30. }
  31. }

  1. shouldInterceptRequest

方法中,你可以返回一个

  1. WebResourceResponse

对象,来改变或替换原始的网络请求,也可以通过默认实现让请求继续执行。

获取网络接口请求的数据(如 API 请求的响应),然后将其返回给 H5,

有以下几种方式可以考虑:

1. 拦截请求并手动发起请求

你可以通过

  1. shouldInterceptRequest

方法拦截 WebView 的 API 请求,自己在 Java 或 Kotlin 中使用

  1. HttpURLConnection

  1. OkHttp

发起网络请求,处理完响应后,再将响应数据传递给 H5。

示例代码:

  1. webView.webViewClient = object : WebViewClient() {
  2. override fun shouldInterceptRequest(
  3. view: WebView,
  4. request: WebResourceRequest
  5. ): WebResourceResponse? {
  6. val url = request.url.toString()
  7. // 判断是否是需要拦截的接口请求
  8. if (url.contains("your_api_endpoint")) {
  9. // 通过 OkHttp 或 HttpURLConnection 发起请求
  10. val response = fetchApiData(url)
  11. // 将获取的响应内容传递给 H5
  12. view.post {
  13. view.evaluateJavascript("javascript:handleApiResponse('${response}')", null)
  14. }
  15. // 返回一个空响应或自定义内容
  16. return WebResourceResponse("application/json", "UTF-8", null)
  17. }
  18. return super.shouldInterceptRequest(view, request)
  19. }
  20. // 使用 OkHttp 发起网络请求(可以根据你的需求选择合适的网络库)
  21. private fun fetchApiData(url: String): String {
  22. // 简单 OkHttp 请求示例
  23. val client = OkHttpClient()
  24. val request = Request.Builder().url(url).build()
  25. client.newCall(request).execute().use { response ->
  26. return response.body?.string() ?: ""
  27. }
  28. }
  29. }

在这个例子中,

  1. shouldInterceptRequest

会拦截指定的网络请求,然后用

  1. OkHttp

发起请求,并将获取的响应数据通过

  1. evaluateJavascript

方法传回到 WebView 中的 H5 页面。

2. 通过 WebView 和 H5 的 JavaScript 通信

另一种方式是通过 WebView 的

  1. addJavascriptInterface

方法,将 Android 代码和 H5 进行通信。当 Android 发起请求后,可以通过接口将数据传递给 H5。

  1. 在 H5 中定义一个 JavaScript 函数,用于接收数据:
  1. function handleApiResponse(response) {
  2. console.log("API Response: ", response);
  3. // 这里可以对响应进行处理,渲染到页面等
  4. }
  1. 2. Android 端:
  1. webView.addJavascriptInterface(object {
  2. @JavascriptInterface
  3. fun sendDataToH5(data: String) {
  4. // 发送数据到 H5
  5. webView.evaluateJavascript("javascript:handleApiResponse('$data')", null)
  6. }
  7. }, "AndroidInterface")
  8. webView.webViewClient = object : WebViewClient() {
  9. override fun shouldInterceptRequest(
  10. view: WebView,
  11. request: WebResourceRequest
  12. ): WebResourceResponse? {
  13. val url = request.url.toString()
  14. if (url.contains("your_api_endpoint")) {
  15. val response = fetchApiData(url)
  16. // 通过 JavaScript 接口传递数据到 H5
  17. view.post {
  18. view.evaluateJavascript("javascript:AndroidInterface.sendDataToH5('${response}')", null)
  19. }
  20. return WebResourceResponse("application/json", "UTF-8", null)
  21. }
  22. return super.shouldInterceptRequest(view, request)
  23. }
  24. }

这种需要考虑到H5 发过来的网络接口请求类型吗?post 还是 get

是的,在使用

  1. shouldInterceptRequest

拦截 H5 发过来的网络请求时,需要考虑请求的类型(GET 或 POST)。这是因为 GET 和 POST 请求的处理方式不同,尤其是 POST 请求,它带有请求体(body)数据,而 GET 请求通常只包含 URL 参数。

  1. shouldInterceptRequest

可以帮助你拦截请求,但对于 POST 请求的处理,需要特别注意提取和使用请求体。

如何区分 GET 和 POST 请求?

在 API 21 及更高版本中,

  1. shouldInterceptRequest

方法中的

  1. WebResourceRequest

对象包含了更多信息,可以通过它的

  1. getMethod()

方法来区分请求类型。

  1. override fun shouldInterceptRequest(
  2. view: WebView,
  3. request: WebResourceRequest
  4. ): WebResourceResponse? {
  5. val url = request.url.toString()
  6. val method = request.method // 获取请求方法,如 GET 或 POST
  7. // 根据请求类型处理
  8. if (method == "POST") {
  9. // 获取 POST 请求的 body(在 Android 的 WebView 中,直接获取 POST body 是不支持的,除非使用特定工具)
  10. // 自定义逻辑处理 POST 请求
  11. } else if (method == "GET") {
  12. // 处理 GET 请求
  13. }
  14. return super.shouldInterceptRequest(view, request)
  15. }

处理 POST 请求

Android 的

  1. shouldInterceptRequest

本身不直接提供获取 POST 请求体的功能。不过你可以使用其他方式来处理,例如通过在 JS 中提前将请求体通过某种方式传递给 Android。

方法 1:使用
  1. evaluateJavascript

传递 POST 请求数据

你可以让 H5 页面在发出 POST 请求前,通过 JavaScript 提前将 POST 请求的数据发送到 WebView。然后 Android 端可以拦截并处理这些数据。

  1. 在 H5 中拦截和传递 POST 请求数据:function interceptAndSendPostData(url, data) { // 调用 Android 接口,将数据传递给 WebView AndroidInterface.sendPostData(url, JSON.stringify(data)); // 继续发起 POST 请求 fetch(url, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" } }).then(response => response.json()) .then(data => console.log(data));}
  2. 在 Android 中接收 POST 数据:webView.addJavascriptInterface(object { @JavascriptInterface fun sendPostData(url: String, data: String) { // 这里可以处理传递过来的 POST 请求数据 Log.d("WebView", "POST 请求 URL: $url, 数据: $data") }}, "AndroidInterface")
方法 2:通过自定义网络层发送 POST 请求

另一种方式是在

  1. shouldInterceptRequest

中拦截 POST 请求后,手动发起 HTTP 请求,处理请求体和响应数据。虽然

  1. shouldInterceptRequest

本身不直接提供请求体,但你可以在 H5 页面中通过 JavaScript 提前将 POST 请求体传递给 Android,或者通过其他网络拦截方式处理(例如使用 OkHttp 的拦截器)。

  1. override fun shouldInterceptRequest(
  2. view: WebView,
  3. request: WebResourceRequest
  4. ): WebResourceResponse? {
  5. val url = request.url.toString()
  6. val method = request.method // 获取请求方法,如 GET 或 POST
  7. if (method == "POST") {
  8. // 自定义发起 HTTP 请求,处理 POST 请求体和响应
  9. val response = performCustomHttpPost(url, request)
  10. return WebResourceResponse("application/json", "UTF-8", response)
  11. }
  12. return super.shouldInterceptRequest(view, request)
  13. }
  14. fun performCustomHttpPost(url: String, request: WebResourceRequest): InputStream {
  15. // 使用 OkHttp 或 HttpURLConnection 发送自定义 POST 请求
  16. val client = OkHttpClient()
  17. val requestBody = ... // 构建请求体
  18. val request = Request.Builder()
  19. .url(url)
  20. .post(requestBody)
  21. .build()
  22. val response = client.newCall(request).execute()
  23. return response.body?.byteStream() ?: ByteArrayInputStream(ByteArray(0))
  24. }

小结

  • GET 请求:你可以直接通过 URL 和请求头信息来拦截和处理。
  • POST 请求:需要特别处理请求体数据。你可以通过 JavaScript 提前将 POST 请求数据传递给 WebView,或通过手动发起 HTTP 请求处理 POST 请求及其响应。

shouldInterceptRequest 能判断出来网络请求的类型吗?比如script xhr 等

  1. shouldInterceptRequest

本身并不能直接判断网络请求的具体类型(如

  1. script

  1. xhr

等),因为它没有提供一个字段明确表明请求是某种类型的资源(如 JavaScript 文件、XHR 请求等)。不过,你可以通过请求的 URL请求头信息 来推断请求的类型。

在 API 21 及以上的 Android 版本中,

  1. WebResourceRequest

对象提供了丰富的信息,包括请求的 URL、请求方法(GET、POST 等)、请求头等,可以根据这些信息推断请求类型。

如何通过 URL 和请求头判断请求类型?

  1. 通过 URL 后缀:- 如果请求的 URL 以 .js 结尾,通常可以认为这是一个 script 请求。- 如果 URL 中包含 /api/ 或类似的标识符,可能是 xhr 请求。- CSS 通常以 .css 结尾,图片文件以 .jpg.png.gif 结尾等。
  2. 通过请求头: 你可以通过请求的头部来推断请求的类型,例如 Accept 头部通常指示客户端期望接收到的数据类型。- Accept: application/json 常用于 xhr 请求。- Accept: text/css 表示请求 CSS 文件。- Accept: application/javascriptAccept: text/javascript 用于 JavaScript 文件请求。

代码示例:如何通过 URL 和请求头推断请求类型

  1. override fun shouldInterceptRequest(
  2. view: WebView,
  3. request: WebResourceRequest
  4. ): WebResourceResponse? {
  5. val url = request.url.toString()
  6. val headers = request.requestHeaders
  7. val acceptHeader = headers["Accept"] // 获取 Accept 头
  8. // 判断是否为脚本请求 (script)
  9. if (url.endsWith(".js") || acceptHeader?.contains("application/javascript") == true) {
  10. Log.d("WebView", "拦截到脚本请求: $url")
  11. // 在此可以拦截或修改脚本请求
  12. }
  13. // 判断是否为 XHR 请求 (通过 URL 或 Accept 头判断)
  14. if (url.contains("/api/") || acceptHeader?.contains("application/json") == true) {
  15. Log.d("WebView", "拦截到 XHR 请求: $url")
  16. // 在此可以拦截或修改 XHR 请求
  17. }
  18. // 判断是否为 CSS 请求
  19. if (url.endsWith(".css") || acceptHeader?.contains("text/css") == true) {
  20. Log.d("WebView", "拦截到 CSS 请求: $url")
  21. // 在此可以拦截或修改 CSS 请求
  22. }
  23. // 其他资源类型的请求
  24. Log.d("WebView", "其他类型的请求: $url")
  25. return super.shouldInterceptRequest(view, request)
  26. }

常见资源类型的判断方式

  • **JavaScript 文件请求 (script)**: - 通过 URL 判断:url.endsWith(".js")- 通过 Accept 头判断:application/javascripttext/javascript
  • XHR 请求: - 通过 URL 规则判断:如果 URL 中包含 /api//ajax/ 或其他常见的 API 路径。- 通过 Accept 头判断:application/json
  • CSS 文件请求: - 通过 URL 判断:url.endsWith(".css")- 通过 Accept 头判断:text/css
  • 图片文件请求: - 通过 URL 判断:url.endsWith(".jpg").png.gif 等- 通过 Accept 头判断:image/*
  • HTML 页面请求: - 通过 Accept 头判断:text/html

小结

虽然

  1. shouldInterceptRequest

没有直接提供资源类型的字段,但你可以通过 URL 的格式和请求头(特别是

  1. Accept

头)来推断请求的类型,从而拦截特定的资源请求。

script xhr font png preflight 分别是什么,应该怎么判断呢?都可以通过 val acceptHeader = headers["Accept"] 来进行判断吗?

  1. Accept

头在 HTTP 请求中指示客户端希望接收的响应数据类型,它确实能在一些情况下帮助我们判断请求的资源类型。但并不是所有类型的请求都会明确使用

  1. Accept

头。因此,我们需要结合 URL请求头 的其他信息(如

  1. Content-Type

  1. Access-Control-Request-Method

等)来进行判断。

1. Script(JavaScript 请求)

JavaScript 请求是用来加载

  1. .js

文件的资源请求。

判断依据:

  • URL 后缀:通常 .js 文件后缀。
  • Acceptapplication/javascripttext/javascript
  1. if (url.endsWith(".js") || acceptHeader?.contains("application/javascript") == true || acceptHeader?.contains("text/javascript") == true) {
  2. Log.d("WebView", "拦截到 JavaScript 请求: $url")
  3. }

2. XHR 请求(XMLHttpRequest / Fetch 请求)

XHR 请求通常用于 AJAX 请求或使用

  1. Fetch API

的异步网络请求。

判断依据:

  • URL 规则:API 请求通常有特定路径,例如 /api//ajax/ 等。
  • Acceptapplication/json(如果返回 JSON 数据)。
  • 请求头:XHR 请求会带有 X-Requested-With: XMLHttpRequest 头部(但不是所有情况都有此头部)。
  1. if (url.contains("/api/") || acceptHeader?.contains("application/json") == true || headers["X-Requested-With"] == "XMLHttpRequest") {
  2. Log.d("WebView", "拦截到 XHR 请求: $url")
  3. }

3. Font 请求(字体文件请求)

字体文件通常以

  1. .woff

  1. .woff2

  1. .ttf

  1. .otf

等后缀结尾。

判断依据:

  • URL 后缀.woff.woff2.ttf.otf
  • Acceptfont/*application/font-woff
  1. if (url.endsWith(".woff") || url.endsWith(".woff2") || url.endsWith(".ttf") || url.endsWith(".otf") || acceptHeader?.contains("font/") == true) {
  2. Log.d("WebView", "拦截到字体文件请求: $url")
  3. }

4. PNG 请求(图片请求)

图片请求通常包括

  1. .png

  1. .jpg

  1. .gif

等格式。

判断依据:

  • URL 后缀:图片文件通常以 .png.jpg.jpeg.gif 结尾。
  • Acceptimage/*
  1. if (url.endsWith(".png") || url.endsWith(".jpg") || url.endsWith(".jpeg") || url.endsWith(".gif") || acceptHeader?.contains("image/") == true) {
  2. Log.d("WebView", "拦截到图片请求: $url")
  3. }

5. Preflight 请求

Preflight 请求是 CORS 请求的一部分,通常在跨域 POST、PUT、DELETE 请求前发送,以确定服务器是否允许该请求。Preflight 请求使用

  1. OPTIONS

方法发起。

判断依据:

  • HTTP 方法OPTIONS(Preflight 请求是 OPTIONS 方法的请求)。
  • 请求头:包含 Access-Control-Request-MethodAccess-Control-Request-Headers
  1. if (request.method == "OPTIONS" && headers.containsKey("Access-Control-Request-Method")) {
  2. Log.d("WebView", "拦截到 Preflight 请求: $url")
  3. }

结合 URL 和

  1. Accept

头进行判断

总结不同请求类型的判断依据:
请求类型URL 后缀

  1. Accept

头其他请求头信息Script (JavaScript)

  1. .js
  1. application/javascript

,

  1. text/javascript

XHR (AJAX)

  1. /api/

,

  1. /ajax/
  1. application/json
  1. X-Requested-With: XMLHttpRequest

Font (字体)

  1. .woff

,

  1. .woff2

,

  1. .ttf

,

  1. .otf
  1. font/*

,

  1. application/font-woff

Image (图片)

  1. .png

,

  1. .jpg

,

  1. .jpeg

,

  1. .gif
  1. image/*

Preflight (CORS)

  1. OPTIONS

方法,

  1. Access-Control-Request-Method

小结

  • Accept 头可以帮助判断请求类型,但并不是所有请求都明确使用它。
  • 根据 URL 后缀、请求方法、其他请求头(如 X-Requested-WithAccess-Control-Request-Method)结合判断能更准确识别不同类型的请求。

返回的响应不符合预期的格式,怎么处理

出现

  1. AxiosError: Network Error, Code: ERR_NETWORK

错误,通常是因为 WebView 在处理拦截的网络请求时,返回的响应不符合预期的格式,或者某些必要的头信息缺失。这里有几个常见的原因及解决办法:

问题可能的原因:

  1. 响应流的格式或处理错误: 你正在返回 response.body()?.byteStream(),但是你还需要确保整个 WebResourceResponse 对象是正确配置的,尤其是内容类型、编码格式以及响应头信息。任何一项出错都可能导致 H5(例如通过 Axios)认为网络请求失败。
  2. 响应头缺失或不完整WebResourceResponse 需要提供一些关键的响应头,比如 Content-TypeContent-Length,这些头在网络请求处理中是必需的。H5 请求的 Axios 需要明确知道响应的长度和类型,否则会认为响应无效。
  3. 返回的流不完整或被提前关闭: 如果返回的流有问题,比如它被提前关闭或者有其他异常情况,可能导致 Axios 在处理时报错。

如何正确返回自定义响应

确保你使用

  1. WebResourceResponse

构建响应时,包括了所有必要的头信息和流数据。

修改代码示例

  1. override fun shouldInterceptRequest(
  2. view: WebView,
  3. request: WebResourceRequest
  4. ): WebResourceResponse? {
  5. val url = request.url.toString()
  6. if (url.contains("your_api_endpoint")) {
  7. // 发起网络请求获取数据
  8. val response = fetchApiData(url)
  9. // 处理返回的响应流
  10. val byteStream = response.body?.byteStream() ?: ByteArrayInputStream(ByteArray(0))
  11. val contentType = response.header("Content-Type", "application/json") // 设置默认内容类型
  12. val contentLength = response.header("Content-Length", "-1").toLong() // 设置内容长度
  13. // 构建 WebResourceResponse 并返回给 WebView
  14. return WebResourceResponse(
  15. contentType, // 内容类型
  16. "utf-8", // 编码格式
  17. 200, // HTTP 状态码
  18. "OK", // 状态描述
  19. mapOf( // 响应头
  20. "Access-Control-Allow-Origin" to "*",
  21. "Content-Length" to contentLength.toString()
  22. ),
  23. byteStream // 响应内容的输入流
  24. )
  25. }
  26. return super.shouldInterceptRequest(view, request)
  27. }
  28. // 使用 OkHttp 发起网络请求
  29. private fun fetchApiData(url: String): Response {
  30. val client = OkHttpClient()
  31. val request = Request.Builder().url(url).build()
  32. return client.newCall(request).execute() // 直接返回 Response 对象
  33. }

关键修改点:

  1. 正确设置内容类型和编码:通过 response.header("Content-Type") 获取原始响应的 Content-Type,并传递给 WebResourceResponse。通常是 application/json
  2. **设置 Content-Length**:通过 response.header("Content-Length") 获取响应内容的长度,并将其传递给 WebView。如果缺少 Content-Length,某些 H5 请求库(如 Axios)可能会报错。
  3. 返回有效的响应流:你需要确保 response.body()?.byteStream() 是有效的。如果网络请求失败或流为空,你可以返回一个空的 ByteArrayInputStream()
  4. 设置状态码和状态描述:如果请求成功,确保返回 200 OK,否则应根据具体情况返回相应的状态码和描述。

检查点

  1. 确保响应流有效:确保 response.body()?.byteStream() 真的有数据,否则 H5 端会认为是无效的响应。你可以通过调试查看响应流的状态。
  2. 正确设置头信息:特别是 Content-TypeContent-Length,它们是网络请求处理的关键。Axios 依赖这些头信息来解析响应。
  3. 错误处理:如果在网络请求过程中发生错误,应返回合适的错误响应,比如 404 Not Found500 Internal Server Error,而不是直接返回空的响应流。

调试建议

  • 通过日志调试输出 OkHttp 请求的状态,确保 fetchApiData 函数返回的 Response 对象中包含有效的数据。
  • 在 Axios 端检查返回的响应头,确保 Content-TypeContent-Length 等信息是正确的。

shouldInterceptRequest 拦截POST请求的时候,如何获取POST 请求的 body 数据?

在 Android 的

  1. WebViewClient

中通过

  1. shouldInterceptRequest

拦截 POST 请求并构建自定义的 HTTP 请求时,首先需要手动构建请求体(如从

  1. WebResourceRequest

中获取必要的请求信息),然后使用

  1. OkHttp

  1. HttpURLConnection

来发送这个 POST 请求。

  1. WebResourceRequest

不直接提供 POST 请求体,因此要获取并手动构建 POST 请求体。这可以通过

  1. requestHeaders

来构建,或在前端(H5)发送时提前传递 POST 数据。

处理 POST 请求并构建请求体的完整示例:

  1. override fun shouldInterceptRequest(
  2. view: WebView,
  3. request: WebResourceRequest
  4. ): WebResourceResponse? {
  5. val url = request.url.toString()
  6. val method = request.method
  7. if (method == "POST") {
  8. Log.d("WebView", "拦截到 POST 请求: $url")
  9. // 自定义处理 POST 请求,构建请求体并发送
  10. val responseStream = performCustomHttpPost(url, request)
  11. // 返回自定义的 WebResourceResponse
  12. return WebResourceResponse(
  13. "application/json", // 假设返回的是 JSON 响应
  14. "UTF-8",
  15. 200, // HTTP 状态码
  16. "OK", // HTTP 状态描述
  17. mapOf("Access-Control-Allow-Origin" to "*"), // 响应头
  18. responseStream // 响应数据的输入流
  19. )
  20. }
  21. return super.shouldInterceptRequest(view, request)
  22. }
  23. // 自定义处理 POST 请求的逻辑
  24. fun performCustomHttpPost(url: String, request: WebResourceRequest): InputStream {
  25. // 构建 POST 请求体(假设 H5 端发送的 POST 数据是 JSON 格式)
  26. val postData = getPostData(request) // 假设这里可以提取请求体数据
  27. // 日志记录请求体数据
  28. Log.d("WebView", "POST 请求数据: $postData")
  29. // 使用 OkHttp 发送自定义的 POST 请求
  30. val client = OkHttpClient()
  31. val requestBody = RequestBody.create(
  32. MediaType.parse("application/json; charset=utf-8"), // 假设请求体是 JSON 数据
  33. postData ?: "" // POST 请求的数据
  34. )
  35. val customRequest = Request.Builder()
  36. .url(url)
  37. .post(requestBody)
  38. .build()
  39. // 发起请求并返回响应流
  40. val response = client.newCall(customRequest).execute()
  41. Log.d("WebView", "HTTP 响应码: ${response.code()}") // 日志记录响应状态码
  42. return response.body?.byteStream() ?: ByteArrayInputStream(ByteArray(0))
  43. }
  44. // 获取 POST 数据(需要通过前端传递或其他方式获取)
  45. fun getPostData(request: WebResourceRequest): String? {
  46. // WebView 无法直接获取 POST body 数据,需要前端配合通过 evaluateJavascript 或其他方式传递
  47. // 假设我们从 requestHeaders 中获取部分数据(实际可以根据你的需求进行修改)
  48. val contentType = request.requestHeaders["Content-Type"]
  49. Log.d("WebView", "Content-Type: $contentType")
  50. // 这里返回模拟的 POST 数据,实际情况下需要处理真实的请求体
  51. return "{ \"key\": \"value\" }"
  52. }
  1. 关键点:
  2. 获取请求体数据:Android WebView 本身不提供获取 POST 请求体的直接方式,需要通过前端协作(如 evaluateJavascript)来传递请求体,或者自行处理模拟请求体。
  3. 手动构建 POST 请求体:使用 OkHttp RequestBody.create() 方法手动构建 POST 请求体。
  4. 记录日志:
  5. 通过 Log.d() 记录拦截的 URL 和请求体数据,便于调试。
  6. 记录 HTTP 响应状态码,查看请求是否成功。
  7. 处理异常情况:如果响应体为空,返回一个空的 ByteArrayInputStream(),以确保 WebView 不会崩溃。
  8. 关于获取真实的 POST 请求体
  9. Android WebView 没有内置机制直接获取 POST 请求体,因此需要通过 JavaScript Android 通信,在 H5 端主动将 POST 数据发送给 WebView。例如:
  1. // 在 H5 端获取 POST 请求体并传递给 Android
  2. function sendPostDataToAndroid(data) {
  3. if (window.AndroidInterface) {
  4. window.AndroidInterface.sendPostData(JSON.stringify(data));
  5. }
  6. }

然后在 Android 端通过

  1. addJavascriptInterface

接收数据:

  1. @JavascriptInterface
  2. fun sendPostData(data: String) {
  3. // 处理从 H5 传递过来的 POST 数据
  4. Log.d("WebView", "收到的 POST 数据: $data")
  5. }

最后

其实获取post请求体参数内容上述的方法可以尝试一下,当然,还有一种更简单的取巧的方法,如果H5 POST请求数量不是很多的话,可以和H5沟通好,直接把请求数据放在请求的url中,中间通过特定字符@隔开,然后我们拿到后进行处理,

  1. // 判断是否为 XHR 请求 (通过 URL 或 Accept 头判断)
  2. if ((url.contains("/api/") || acceptHeader?.contains("application/json") == true ||
  3. headers["X-Requested-With"] == "XMLHttpRequest") && !url.endsWith("html")) {
  4. LogUtils.i( "拦截到 XHR 请求: $url")
  5. // 在此可以拦截或修改 XHR 请求
  6. var respone: Response? = null
  7. if(method == "POST"){
  8. val params = url.split("@")
  9. val test = params[0] // 获取 POST 请求的 body 数据
  10. val postData = URLDecoder.decode(params[1], StandardCharsets.UTF_8.toString())
  11. LogUtils.i("postData = $postData")
  12. respone = fetchApiData2(test,method,postData)
  13. }else if(method == "GET"){
  14. respone = fetchApiData(url,method,null)
  15. }
  16. val byteStream = respone?.body()?.byteStream() ?: ByteArrayInputStream(ByteArray(0))
  17. val contentType = respone?.header("Content-Type", "application/json") // 设置默认内容类型
  18. val contentLength = respone?.header("Content-Length", "-1")?.toLong() // 设置内容长度
  19. LogUtils.i("fetchApiData respone = ${respone.toString()}")
  20. return WebResourceResponse(
  21. contentType, // 内容类型
  22. "utf-8", // 编码格式
  23. 200, // HTTP 状态码
  24. "OK", // 状态描述
  25. mapOf( // 响应头
  26. "Access-Control-Allow-Origin" to "*",
  27. "Content-Length" to contentLength.toString()
  28. ),
  29. byteStream // 响应内容的输入流
  30. )
  31. // return WebResourceResponse("application/json", "utf-8",respone)
  32. }
  1. private fun fetchApiData2(url: String, method: String, postData: String?): Response{
  2. LogUtils.i("fetchApiData2 = $url + $method + $postData")
  3. val client = UnsafeOkHttpClient.unsafeOkHttpClient.build()
  4. val requestBody = RequestBody.create(
  5. MediaType.parse("application/json; charset=utf-8"), // 假设请求体是 JSON 数据
  6. postData ?: "" // POST 请求的数据
  7. )
  8. val requestBuilder = Request.Builder()
  9. .url(url)
  10. .post(requestBody)
  11. .build()
  12. return client.newCall(requestBuilder).execute()
  13. }
  1. // 根据请求类型进行处理
  2. private fun fetchApiData(url: String, method: String, postData: Map<String, String>?): Response {
  3. LogUtils.i("fetchApiData = $url + $method + $postData")
  4. val client = UnsafeOkHttpClient.unsafeOkHttpClient.build()
  5. val requestBuilder = Request.Builder().url(url)
  6. // 根据请求类型构建请求
  7. if (method == "POST" && postData != null) {
  8. val formBody = FormBody.Builder()
  9. postData.forEach { (key, value) -> formBody.add(key, value) }
  10. requestBuilder.post(formBody.build())
  11. }
  12. val request = requestBuilder.build()
  13. return client.newCall(request).execute() // 直接返回 Response 对象
  14. }
标签: android

本文转载自: https://blog.csdn.net/gqg_guan/article/details/142492872
版权归原作者 大渔歌_ 所有, 如有侵权,请联系我们删除。

“Android webview拦截H5的接口请求并返回处理好的数据”的评论:

还没有评论