Android?webview攔截H5的接口請(qǐng)求并返回處理好的數(shù)據(jù)代碼示例
Android webview攔截H5的接口請(qǐng)求并返回處理好的數(shù)據(jù)
Android 可以通過(guò) WebView
的 shouldInterceptRequest
方法攔截到 H5 中的網(wǎng)絡(luò)請(qǐng)求。這是一個(gè) WebViewClient
中的回調(diào)方法,允許開(kāi)發(fā)者在 WebView 發(fā)起網(wǎng)絡(luò)請(qǐng)求時(shí)對(duì)其進(jìn)行處理和修改。
具體使用方法如下:
你需要?jiǎng)?chuàng)建一個(gè)自定義的
WebViewClient
,并重寫(xiě)shouldInterceptRequest
方法。在該方法中,你可以攔截 WebView 發(fā)起的網(wǎng)絡(luò)請(qǐng)求,并返回一個(gè)自定義的響應(yīng),或讓請(qǐng)求繼續(xù)。
該方法在 API 21 (Android 5.0) 及更高版本中引入了兩個(gè)重載方法:
shouldInterceptRequest(WebView view, String url)
(API 11)shouldInterceptRequest(WebView view, WebResourceRequest request)
(API 21)
以下是代碼示例:
// Kotlin 代碼示例 webView.webViewClient = object : WebViewClient() { // 對(duì)于 API 21 及更高版本 override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = request.url.toString() // 這里你可以判斷 URL,并根據(jù)需要攔截或修改請(qǐng)求 if (url.contains("your_target_url")) { // 可以在這里做一些處理,例如替換請(qǐng)求或返回本地?cái)?shù)據(jù) val inputStream = ... // 自定義輸入流 return WebResourceResponse("text/html", "UTF-8", inputStream) } // 不攔截,繼續(xù)請(qǐng)求 return super.shouldInterceptRequest(view, request) } // 對(duì)于 API 11 到 API 20 的設(shè)備 override fun shouldInterceptRequest( view: WebView, url: String ): WebResourceResponse? { // 這里處理邏輯類(lèi)似于上面的代碼 if (url.contains("your_target_url")) { // 自定義處理邏輯 val inputStream = ... return WebResourceResponse("text/html", "UTF-8", inputStream) } return super.shouldInterceptRequest(view, url) } }
在 shouldInterceptRequest
方法中,你可以返回一個(gè) WebResourceResponse
對(duì)象,來(lái)改變或替換原始的網(wǎng)絡(luò)請(qǐng)求,也可以通過(guò)默認(rèn)實(shí)現(xiàn)讓請(qǐng)求繼續(xù)執(zhí)行。
獲取網(wǎng)絡(luò)接口請(qǐng)求的數(shù)據(jù)(如 API 請(qǐng)求的響應(yīng)),然后將其返回給 H5,
有以下幾種方式可以考慮:
1. 攔截請(qǐng)求并手動(dòng)發(fā)起請(qǐng)求
你可以通過(guò) shouldInterceptRequest
方法攔截 WebView 的 API 請(qǐng)求,自己在 Java 或 Kotlin 中使用 HttpURLConnection
或 OkHttp
發(fā)起網(wǎng)絡(luò)請(qǐng)求,處理完響應(yīng)后,再將響應(yīng)數(shù)據(jù)傳遞給 H5。
示例代碼:
webView.webViewClient = object : WebViewClient() { override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = request.url.toString() // 判斷是否是需要攔截的接口請(qǐng)求 if (url.contains("your_api_endpoint")) { // 通過(guò) OkHttp 或 HttpURLConnection 發(fā)起請(qǐng)求 val response = fetchApiData(url) // 將獲取的響應(yīng)內(nèi)容傳遞給 H5 view.post { view.evaluateJavascript("javascript:handleApiResponse('${response}')", null) } // 返回一個(gè)空響應(yīng)或自定義內(nèi)容 return WebResourceResponse("application/json", "UTF-8", null) } return super.shouldInterceptRequest(view, request) } // 使用 OkHttp 發(fā)起網(wǎng)絡(luò)請(qǐng)求(可以根據(jù)你的需求選擇合適的網(wǎng)絡(luò)庫(kù)) private fun fetchApiData(url: String): String { // 簡(jiǎn)單 OkHttp 請(qǐng)求示例 val client = OkHttpClient() val request = Request.Builder().url(url).build() client.newCall(request).execute().use { response -> return response.body?.string() ?: "" } } }
在這個(gè)例子中,shouldInterceptRequest
會(huì)攔截指定的網(wǎng)絡(luò)請(qǐng)求,然后用 OkHttp
發(fā)起請(qǐng)求,并將獲取的響應(yīng)數(shù)據(jù)通過(guò) evaluateJavascript
方法傳回到 WebView 中的 H5 頁(yè)面。
2. 通過(guò) WebView 和 H5 的 JavaScript 通信
另一種方式是通過(guò) WebView 的 addJavascriptInterface
方法,將 Android 代碼和 H5 進(jìn)行通信。當(dāng) Android 發(fā)起請(qǐng)求后,可以通過(guò)接口將數(shù)據(jù)傳遞給 H5。
在 H5 中定義一個(gè) JavaScript 函數(shù),用于接收數(shù)據(jù):
function handleApiResponse(response) { console.log("API Response: ", response); // 這里可以對(duì)響應(yīng)進(jìn)行處理,渲染到頁(yè)面等 }
在 Android 端:
webView.addJavascriptInterface(object { @JavascriptInterface fun sendDataToH5(data: String) { // 發(fā)送數(shù)據(jù)到 H5 webView.evaluateJavascript("javascript:handleApiResponse('$data')", null) } }, "AndroidInterface") webView.webViewClient = object : WebViewClient() { override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = request.url.toString() if (url.contains("your_api_endpoint")) { val response = fetchApiData(url) // 通過(guò) JavaScript 接口傳遞數(shù)據(jù)到 H5 view.post { view.evaluateJavascript("javascript:AndroidInterface.sendDataToH5('${response}')", null) } return WebResourceResponse("application/json", "UTF-8", null) } return super.shouldInterceptRequest(view, request) } }
這種需要考慮到H5 發(fā)過(guò)來(lái)的網(wǎng)絡(luò)接口請(qǐng)求類(lèi)型嗎?post 還是 get
是的,在使用 shouldInterceptRequest
攔截 H5 發(fā)過(guò)來(lái)的網(wǎng)絡(luò)請(qǐng)求時(shí),需要考慮請(qǐng)求的類(lèi)型(GET 或 POST)。這是因?yàn)?GET 和 POST 請(qǐng)求的處理方式不同,尤其是 POST 請(qǐng)求,它帶有請(qǐng)求體(body)數(shù)據(jù),而 GET 請(qǐng)求通常只包含 URL 參數(shù)。
shouldInterceptRequest
可以幫助你攔截請(qǐng)求,但對(duì)于 POST 請(qǐng)求的處理,需要特別注意提取和使用請(qǐng)求體。
如何區(qū)分 GET 和 POST 請(qǐng)求?
在 API 21 及更高版本中,shouldInterceptRequest
方法中的 WebResourceRequest
對(duì)象包含了更多信息,可以通過(guò)它的 getMethod()
方法來(lái)區(qū)分請(qǐng)求類(lèi)型。
override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = request.url.toString() val method = request.method // 獲取請(qǐng)求方法,如 GET 或 POST // 根據(jù)請(qǐng)求類(lèi)型處理 if (method == "POST") { // 獲取 POST 請(qǐng)求的 body(在 Android 的 WebView 中,直接獲取 POST body 是不支持的,除非使用特定工具) // 自定義邏輯處理 POST 請(qǐng)求 } else if (method == "GET") { // 處理 GET 請(qǐng)求 } return super.shouldInterceptRequest(view, request) }
處理 POST 請(qǐng)求
Android 的 shouldInterceptRequest
本身不直接提供獲取 POST 請(qǐng)求體的功能。不過(guò)你可以使用其他方式來(lái)處理,例如通過(guò)在 JS 中提前將請(qǐng)求體通過(guò)某種方式傳遞給 Android。
方法 1:使用 evaluateJavascript 傳遞 POST 請(qǐng)求數(shù)據(jù)
你可以讓 H5 頁(yè)面在發(fā)出 POST 請(qǐng)求前,通過(guò) JavaScript 提前將 POST 請(qǐng)求的數(shù)據(jù)發(fā)送到 WebView。然后 Android 端可以攔截并處理這些數(shù)據(jù)。
在 H5 中攔截和傳遞 POST 請(qǐng)求數(shù)據(jù):
function interceptAndSendPostData(url, data) { // 調(diào)用 Android 接口,將數(shù)據(jù)傳遞給 WebView AndroidInterface.sendPostData(url, JSON.stringify(data)); // 繼續(xù)發(fā)起 POST 請(qǐng)求 fetch(url, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" } }).then(response => response.json()) .then(data => console.log(data)); }
在 Android 中接收 POST 數(shù)據(jù):
webView.addJavascriptInterface(object { @JavascriptInterface fun sendPostData(url: String, data: String) { // 這里可以處理傳遞過(guò)來(lái)的 POST 請(qǐng)求數(shù)據(jù) Log.d("WebView", "POST 請(qǐng)求 URL: $url, 數(shù)據(jù): $data") } }, "AndroidInterface")
方法 2:通過(guò)自定義網(wǎng)絡(luò)層發(fā)送 POST 請(qǐng)求
另一種方式是在 shouldInterceptRequest
中攔截 POST 請(qǐng)求后,手動(dòng)發(fā)起 HTTP 請(qǐng)求,處理請(qǐng)求體和響應(yīng)數(shù)據(jù)。雖然 shouldInterceptRequest
本身不直接提供請(qǐng)求體,但你可以在 H5 頁(yè)面中通過(guò) JavaScript 提前將 POST 請(qǐng)求體傳遞給 Android,或者通過(guò)其他網(wǎng)絡(luò)攔截方式處理(例如使用 OkHttp 的攔截器)。
override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = request.url.toString() val method = request.method // 獲取請(qǐng)求方法,如 GET 或 POST if (method == "POST") { // 自定義發(fā)起 HTTP 請(qǐng)求,處理 POST 請(qǐng)求體和響應(yīng) val response = performCustomHttpPost(url, request) return WebResourceResponse("application/json", "UTF-8", response) } return super.shouldInterceptRequest(view, request) } fun performCustomHttpPost(url: String, request: WebResourceRequest): InputStream { // 使用 OkHttp 或 HttpURLConnection 發(fā)送自定義 POST 請(qǐng)求 val client = OkHttpClient() val requestBody = ... // 構(gòu)建請(qǐng)求體 val request = Request.Builder() .url(url) .post(requestBody) .build() val response = client.newCall(request).execute() return response.body?.byteStream() ?: ByteArrayInputStream(ByteArray(0)) }
小結(jié)
- GET 請(qǐng)求:你可以直接通過(guò) URL 和請(qǐng)求頭信息來(lái)攔截和處理。
- POST 請(qǐng)求:需要特別處理請(qǐng)求體數(shù)據(jù)。你可以通過(guò) JavaScript 提前將 POST 請(qǐng)求數(shù)據(jù)傳遞給 WebView,或通過(guò)手動(dòng)發(fā)起 HTTP 請(qǐng)求處理 POST 請(qǐng)求及其響應(yīng)。
shouldInterceptRequest 能判斷出來(lái)網(wǎng)絡(luò)請(qǐng)求的類(lèi)型嗎?比如script xhr 等
shouldInterceptRequest
本身并不能直接判斷網(wǎng)絡(luò)請(qǐng)求的具體類(lèi)型(如 script
、xhr
等),因?yàn)樗鼪](méi)有提供一個(gè)字段明確表明請(qǐng)求是某種類(lèi)型的資源(如 JavaScript 文件、XHR 請(qǐng)求等)。不過(guò),你可以通過(guò)請(qǐng)求的 URL 和 請(qǐng)求頭信息 來(lái)推斷請(qǐng)求的類(lèi)型。
在 API 21 及以上的 Android 版本中,WebResourceRequest
對(duì)象提供了豐富的信息,包括請(qǐng)求的 URL、請(qǐng)求方法(GET、POST 等)、請(qǐng)求頭等,可以根據(jù)這些信息推斷請(qǐng)求類(lèi)型。
如何通過(guò) URL 和請(qǐng)求頭判斷請(qǐng)求類(lèi)型?
通過(guò) URL 后綴:
- 如果請(qǐng)求的 URL 以
.js
結(jié)尾,通??梢哉J(rèn)為這是一個(gè)script
請(qǐng)求。 - 如果 URL 中包含
/api/
或類(lèi)似的標(biāo)識(shí)符,可能是xhr
請(qǐng)求。 - CSS 通常以
.css
結(jié)尾,圖片文件以.jpg
、.png
、.gif
結(jié)尾等。
- 如果請(qǐng)求的 URL 以
通過(guò)請(qǐng)求頭: 你可以通過(guò)請(qǐng)求的頭部來(lái)推斷請(qǐng)求的類(lèi)型,例如
Accept
頭部通常指示客戶端期望接收到的數(shù)據(jù)類(lèi)型。Accept: application/json
常用于xhr
請(qǐng)求。Accept: text/css
表示請(qǐng)求 CSS 文件。Accept: application/javascript
或Accept: text/javascript
用于 JavaScript 文件請(qǐng)求。
代碼示例:如何通過(guò) URL 和請(qǐng)求頭推斷請(qǐng)求類(lèi)型
override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = request.url.toString() val headers = request.requestHeaders val acceptHeader = headers["Accept"] // 獲取 Accept 頭 // 判斷是否為腳本請(qǐng)求 (script) if (url.endsWith(".js") || acceptHeader?.contains("application/javascript") == true) { Log.d("WebView", "攔截到腳本請(qǐng)求: $url") // 在此可以攔截或修改腳本請(qǐng)求 } // 判斷是否為 XHR 請(qǐng)求 (通過(guò) URL 或 Accept 頭判斷) if (url.contains("/api/") || acceptHeader?.contains("application/json") == true) { Log.d("WebView", "攔截到 XHR 請(qǐng)求: $url") // 在此可以攔截或修改 XHR 請(qǐng)求 } // 判斷是否為 CSS 請(qǐng)求 if (url.endsWith(".css") || acceptHeader?.contains("text/css") == true) { Log.d("WebView", "攔截到 CSS 請(qǐng)求: $url") // 在此可以攔截或修改 CSS 請(qǐng)求 } // 其他資源類(lèi)型的請(qǐng)求 Log.d("WebView", "其他類(lèi)型的請(qǐng)求: $url") return super.shouldInterceptRequest(view, request) }
常見(jiàn)資源類(lèi)型的判斷方式
- JavaScript 文件請(qǐng)求 (
script
):- 通過(guò) URL 判斷:
url.endsWith(".js")
- 通過(guò)
Accept
頭判斷:application/javascript
或text/javascript
- 通過(guò) URL 判斷:
- XHR 請(qǐng)求:
- 通過(guò) URL 規(guī)則判斷:如果 URL 中包含
/api/
、/ajax/
或其他常見(jiàn)的 API 路徑。 - 通過(guò)
Accept
頭判斷:application/json
- 通過(guò) URL 規(guī)則判斷:如果 URL 中包含
- CSS 文件請(qǐng)求:
- 通過(guò) URL 判斷:
url.endsWith(".css")
- 通過(guò)
Accept
頭判斷:text/css
- 通過(guò) URL 判斷:
- 圖片文件請(qǐng)求:
- 通過(guò) URL 判斷:
url.endsWith(".jpg")
、.png
、.gif
等 - 通過(guò)
Accept
頭判斷:image/*
- 通過(guò) URL 判斷:
- HTML 頁(yè)面請(qǐng)求:
- 通過(guò)
Accept
頭判斷:text/html
- 通過(guò)
小結(jié)
雖然 shouldInterceptRequest
沒(méi)有直接提供資源類(lèi)型的字段,但你可以通過(guò) URL 的格式和請(qǐng)求頭(特別是 Accept
頭)來(lái)推斷請(qǐng)求的類(lèi)型,從而攔截特定的資源請(qǐng)求。
script xhr font png preflight 分別是什么,應(yīng)該怎么判斷呢?都可以通過(guò) val acceptHeader = headers["Accept"] 來(lái)進(jìn)行判斷嗎?
Accept
頭在 HTTP 請(qǐng)求中指示客戶端希望接收的響應(yīng)數(shù)據(jù)類(lèi)型,它確實(shí)能在一些情況下幫助我們判斷請(qǐng)求的資源類(lèi)型。但并不是所有類(lèi)型的請(qǐng)求都會(huì)明確使用 Accept
頭。因此,我們需要結(jié)合 URL 和 請(qǐng)求頭 的其他信息(如 Content-Type
、Access-Control-Request-Method
等)來(lái)進(jìn)行判斷。
1. Script(JavaScript 請(qǐng)求)
JavaScript 請(qǐng)求是用來(lái)加載 .js
文件的資源請(qǐng)求。
判斷依據(jù):
- URL 后綴:通常
.js
文件后綴。 Accept
頭:application/javascript
或text/javascript
。
if (url.endsWith(".js") || acceptHeader?.contains("application/javascript") == true || acceptHeader?.contains("text/javascript") == true) { Log.d("WebView", "攔截到 JavaScript 請(qǐng)求: $url") }
2. XHR 請(qǐng)求(XMLHttpRequest / Fetch 請(qǐng)求)
XHR 請(qǐng)求通常用于 AJAX 請(qǐng)求或使用 Fetch API
的異步網(wǎng)絡(luò)請(qǐng)求。
判斷依據(jù):
- URL 規(guī)則:API 請(qǐng)求通常有特定路徑,例如
/api/
、/ajax/
等。 Accept
頭:application/json
(如果返回 JSON 數(shù)據(jù))。- 請(qǐng)求頭:XHR 請(qǐng)求會(huì)帶有
X-Requested-With: XMLHttpRequest
頭部(但不是所有情況都有此頭部)。
if (url.contains("/api/") || acceptHeader?.contains("application/json") == true || headers["X-Requested-With"] == "XMLHttpRequest") { Log.d("WebView", "攔截到 XHR 請(qǐng)求: $url") }
3. Font 請(qǐng)求(字體文件請(qǐng)求)
字體文件通常以 .woff
、.woff2
、.ttf
、.otf
等后綴結(jié)尾。
判斷依據(jù):
- URL 后綴:
.woff
、.woff2
、.ttf
、.otf
。 Accept
頭:font/*
或application/font-woff
。
if (url.endsWith(".woff") || url.endsWith(".woff2") || url.endsWith(".ttf") || url.endsWith(".otf") || acceptHeader?.contains("font/") == true) { Log.d("WebView", "攔截到字體文件請(qǐng)求: $url") }
4. PNG 請(qǐng)求(圖片請(qǐng)求)
圖片請(qǐng)求通常包括 .png
、.jpg
、.gif
等格式。
判斷依據(jù):
- URL 后綴:圖片文件通常以
.png
、.jpg
、.jpeg
、.gif
結(jié)尾。 Accept
頭:image/*
。
if (url.endsWith(".png") || url.endsWith(".jpg") || url.endsWith(".jpeg") || url.endsWith(".gif") || acceptHeader?.contains("image/") == true) { Log.d("WebView", "攔截到圖片請(qǐng)求: $url") }
5. Preflight 請(qǐng)求
Preflight 請(qǐng)求是 CORS 請(qǐng)求的一部分,通常在跨域 POST、PUT、DELETE 請(qǐng)求前發(fā)送,以確定服務(wù)器是否允許該請(qǐng)求。Preflight 請(qǐng)求使用 OPTIONS
方法發(fā)起。
判斷依據(jù):
- HTTP 方法:
OPTIONS
(Preflight 請(qǐng)求是OPTIONS
方法的請(qǐng)求)。 - 請(qǐng)求頭:包含
Access-Control-Request-Method
和Access-Control-Request-Headers
。
if (request.method == "OPTIONS" && headers.containsKey("Access-Control-Request-Method")) { Log.d("WebView", "攔截到 Preflight 請(qǐng)求: $url") }
結(jié)合 URL 和 Accept 頭進(jìn)行判斷
總結(jié)不同請(qǐng)求類(lèi)型的判斷依據(jù):
請(qǐng)求類(lèi)型 | URL 后綴 | Accept 頭 | 其他請(qǐng)求頭信息 |
---|---|---|---|
Script (JavaScript) | .js | application/javascript , text/javascript | |
XHR (AJAX) | /api/ , /ajax/ | application/json | X-Requested-With: XMLHttpRequest |
Font (字體) | .woff , .woff2 , .ttf , .otf | font/* , application/font-woff | |
Image (圖片) | .png , .jpg , .jpeg , .gif | image/* | |
Preflight (CORS) | OPTIONS 方法,Access-Control-Request-Method |
小結(jié)
Accept
頭可以幫助判斷請(qǐng)求類(lèi)型,但并不是所有請(qǐng)求都明確使用它。- 根據(jù) URL 后綴、請(qǐng)求方法、其他請(qǐng)求頭(如
X-Requested-With
、Access-Control-Request-Method
)結(jié)合判斷能更準(zhǔn)確識(shí)別不同類(lèi)型的請(qǐng)求。
返回的響應(yīng)不符合預(yù)期的格式,怎么處理
出現(xiàn) AxiosError: Network Error, Code: ERR_NETWORK
錯(cuò)誤,通常是因?yàn)?WebView 在處理攔截的網(wǎng)絡(luò)請(qǐng)求時(shí),返回的響應(yīng)不符合預(yù)期的格式,或者某些必要的頭信息缺失。這里有幾個(gè)常見(jiàn)的原因及解決辦法:
問(wèn)題可能的原因:
響應(yīng)流的格式或處理錯(cuò)誤: 你正在返回
response.body()?.byteStream()
,但是你還需要確保整個(gè)WebResourceResponse
對(duì)象是正確配置的,尤其是內(nèi)容類(lèi)型、編碼格式以及響應(yīng)頭信息。任何一項(xiàng)出錯(cuò)都可能導(dǎo)致 H5(例如通過(guò) Axios)認(rèn)為網(wǎng)絡(luò)請(qǐng)求失敗。響應(yīng)頭缺失或不完整:
WebResourceResponse
需要提供一些關(guān)鍵的響應(yīng)頭,比如Content-Type
和Content-Length
,這些頭在網(wǎng)絡(luò)請(qǐng)求處理中是必需的。H5 請(qǐng)求的 Axios 需要明確知道響應(yīng)的長(zhǎng)度和類(lèi)型,否則會(huì)認(rèn)為響應(yīng)無(wú)效。返回的流不完整或被提前關(guān)閉: 如果返回的流有問(wèn)題,比如它被提前關(guān)閉或者有其他異常情況,可能導(dǎo)致 Axios 在處理時(shí)報(bào)錯(cuò)。
如何正確返回自定義響應(yīng)
確保你使用 WebResourceResponse
構(gòu)建響應(yīng)時(shí),包括了所有必要的頭信息和流數(shù)據(jù)。
修改代碼示例
override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = request.url.toString() if (url.contains("your_api_endpoint")) { // 發(fā)起網(wǎng)絡(luò)請(qǐng)求獲取數(shù)據(jù) val response = fetchApiData(url) // 處理返回的響應(yīng)流 val byteStream = response.body?.byteStream() ?: ByteArrayInputStream(ByteArray(0)) val contentType = response.header("Content-Type", "application/json") // 設(shè)置默認(rèn)內(nèi)容類(lèi)型 val contentLength = response.header("Content-Length", "-1").toLong() // 設(shè)置內(nèi)容長(zhǎng)度 // 構(gòu)建 WebResourceResponse 并返回給 WebView return WebResourceResponse( contentType, // 內(nèi)容類(lèi)型 "utf-8", // 編碼格式 200, // HTTP 狀態(tài)碼 "OK", // 狀態(tài)描述 mapOf( // 響應(yīng)頭 "Access-Control-Allow-Origin" to "*", "Content-Length" to contentLength.toString() ), byteStream // 響應(yīng)內(nèi)容的輸入流 ) } return super.shouldInterceptRequest(view, request) } // 使用 OkHttp 發(fā)起網(wǎng)絡(luò)請(qǐng)求 private fun fetchApiData(url: String): Response { val client = OkHttpClient() val request = Request.Builder().url(url).build() return client.newCall(request).execute() // 直接返回 Response 對(duì)象 }
關(guān)鍵修改點(diǎn):
正確設(shè)置內(nèi)容類(lèi)型和編碼:通過(guò)
response.header("Content-Type")
獲取原始響應(yīng)的Content-Type
,并傳遞給WebResourceResponse
。通常是application/json
。設(shè)置
Content-Length
:通過(guò)response.header("Content-Length")
獲取響應(yīng)內(nèi)容的長(zhǎng)度,并將其傳遞給 WebView。如果缺少Content-Length
,某些 H5 請(qǐng)求庫(kù)(如 Axios)可能會(huì)報(bào)錯(cuò)。返回有效的響應(yīng)流:你需要確保
response.body()?.byteStream()
是有效的。如果網(wǎng)絡(luò)請(qǐng)求失敗或流為空,你可以返回一個(gè)空的ByteArrayInputStream()
。設(shè)置狀態(tài)碼和狀態(tài)描述:如果請(qǐng)求成功,確保返回
200 OK
,否則應(yīng)根據(jù)具體情況返回相應(yīng)的狀態(tài)碼和描述。
檢查點(diǎn)
確保響應(yīng)流有效:確保
response.body()?.byteStream()
真的有數(shù)據(jù),否則 H5 端會(huì)認(rèn)為是無(wú)效的響應(yīng)。你可以通過(guò)調(diào)試查看響應(yīng)流的狀態(tài)。正確設(shè)置頭信息:特別是
Content-Type
和Content-Length
,它們是網(wǎng)絡(luò)請(qǐng)求處理的關(guān)鍵。Axios 依賴這些頭信息來(lái)解析響應(yīng)。錯(cuò)誤處理:如果在網(wǎng)絡(luò)請(qǐng)求過(guò)程中發(fā)生錯(cuò)誤,應(yīng)返回合適的錯(cuò)誤響應(yīng),比如
404 Not Found
或500 Internal Server Error
,而不是直接返回空的響應(yīng)流。
調(diào)試建議
- 通過(guò)日志調(diào)試輸出 OkHttp 請(qǐng)求的狀態(tài),確保
fetchApiData
函數(shù)返回的Response
對(duì)象中包含有效的數(shù)據(jù)。 - 在 Axios 端檢查返回的響應(yīng)頭,確保
Content-Type
、Content-Length
等信息是正確的。
shouldInterceptRequest 攔截POST請(qǐng)求的時(shí)候,如何獲取POST 請(qǐng)求的 body 數(shù)據(jù)?
在 Android 的 WebViewClient
中通過(guò) shouldInterceptRequest
攔截 POST 請(qǐng)求并構(gòu)建自定義的 HTTP 請(qǐng)求時(shí),首先需要手動(dòng)構(gòu)建請(qǐng)求體(如從 WebResourceRequest
中獲取必要的請(qǐng)求信息),然后使用 OkHttp
或 HttpURLConnection
來(lái)發(fā)送這個(gè) POST 請(qǐng)求。
WebResourceRequest
不直接提供 POST 請(qǐng)求體,因此要獲取并手動(dòng)構(gòu)建 POST 請(qǐng)求體。這可以通過(guò) requestHeaders
來(lái)構(gòu)建,或在前端(H5)發(fā)送時(shí)提前傳遞 POST 數(shù)據(jù)。
處理 POST 請(qǐng)求并構(gòu)建請(qǐng)求體的完整示例:
override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = request.url.toString() val method = request.method if (method == "POST") { Log.d("WebView", "攔截到 POST 請(qǐng)求: $url") // 自定義處理 POST 請(qǐng)求,構(gòu)建請(qǐng)求體并發(fā)送 val responseStream = performCustomHttpPost(url, request) // 返回自定義的 WebResourceResponse return WebResourceResponse( "application/json", // 假設(shè)返回的是 JSON 響應(yīng) "UTF-8", 200, // HTTP 狀態(tài)碼 "OK", // HTTP 狀態(tài)描述 mapOf("Access-Control-Allow-Origin" to "*"), // 響應(yīng)頭 responseStream // 響應(yīng)數(shù)據(jù)的輸入流 ) } return super.shouldInterceptRequest(view, request) } // 自定義處理 POST 請(qǐng)求的邏輯 fun performCustomHttpPost(url: String, request: WebResourceRequest): InputStream { // 構(gòu)建 POST 請(qǐng)求體(假設(shè) H5 端發(fā)送的 POST 數(shù)據(jù)是 JSON 格式) val postData = getPostData(request) // 假設(shè)這里可以提取請(qǐng)求體數(shù)據(jù) // 日志記錄請(qǐng)求體數(shù)據(jù) Log.d("WebView", "POST 請(qǐng)求數(shù)據(jù): $postData") // 使用 OkHttp 發(fā)送自定義的 POST 請(qǐng)求 val client = OkHttpClient() val requestBody = RequestBody.create( MediaType.parse("application/json; charset=utf-8"), // 假設(shè)請(qǐng)求體是 JSON 數(shù)據(jù) postData ?: "" // POST 請(qǐng)求的數(shù)據(jù) ) val customRequest = Request.Builder() .url(url) .post(requestBody) .build() // 發(fā)起請(qǐng)求并返回響應(yīng)流 val response = client.newCall(customRequest).execute() Log.d("WebView", "HTTP 響應(yīng)碼: ${response.code()}") // 日志記錄響應(yīng)狀態(tài)碼 return response.body?.byteStream() ?: ByteArrayInputStream(ByteArray(0)) } // 獲取 POST 數(shù)據(jù)(需要通過(guò)前端傳遞或其他方式獲?。? fun getPostData(request: WebResourceRequest): String? { // WebView 無(wú)法直接獲取 POST body 數(shù)據(jù),需要前端配合通過(guò) evaluateJavascript 或其他方式傳遞 // 假設(shè)我們從 requestHeaders 中獲取部分?jǐn)?shù)據(jù)(實(shí)際可以根據(jù)你的需求進(jìn)行修改) val contentType = request.requestHeaders["Content-Type"] Log.d("WebView", "Content-Type: $contentType") // 這里返回模擬的 POST 數(shù)據(jù),實(shí)際情況下需要處理真實(shí)的請(qǐng)求體 return "{ \"key\": \"value\" }" }
關(guān)鍵點(diǎn): 獲取請(qǐng)求體數(shù)據(jù):Android WebView 本身不提供獲取 POST 請(qǐng)求體的直接方式,需要通過(guò)前端協(xié)作(如 evaluateJavascript)來(lái)傳遞請(qǐng)求體,或者自行處理模擬請(qǐng)求體。 手動(dòng)構(gòu)建 POST 請(qǐng)求體:使用 OkHttp 的 RequestBody.create() 方法手動(dòng)構(gòu)建 POST 請(qǐng)求體。 記錄日志: 通過(guò) Log.d() 記錄攔截的 URL 和請(qǐng)求體數(shù)據(jù),便于調(diào)試。 記錄 HTTP 響應(yīng)狀態(tài)碼,查看請(qǐng)求是否成功。 處理異常情況:如果響應(yīng)體為空,返回一個(gè)空的 ByteArrayInputStream(),以確保 WebView 不會(huì)崩潰。 關(guān)于獲取真實(shí)的 POST 請(qǐng)求體 Android 的 WebView 沒(méi)有內(nèi)置機(jī)制直接獲取 POST 請(qǐng)求體,因此需要通過(guò) JavaScript 與 Android 通信,在 H5 端主動(dòng)將 POST 數(shù)據(jù)發(fā)送給 WebView。例如:
// 在 H5 端獲取 POST 請(qǐng)求體并傳遞給 Android function sendPostDataToAndroid(data) { if (window.AndroidInterface) { window.AndroidInterface.sendPostData(JSON.stringify(data)); } }
然后在 Android 端通過(guò) addJavascriptInterface
接收數(shù)據(jù):
@JavascriptInterface fun sendPostData(data: String) { // 處理從 H5 傳遞過(guò)來(lái)的 POST 數(shù)據(jù) Log.d("WebView", "收到的 POST 數(shù)據(jù): $data") }
最后
其實(shí)獲取post請(qǐng)求體參數(shù)內(nèi)容上述的方法可以嘗試一下,當(dāng)然,還有一種更簡(jiǎn)單的取巧的方法,如果H5 POST請(qǐng)求數(shù)量不是很多的話,可以和H5溝通好,直接把請(qǐng)求數(shù)據(jù)放在請(qǐng)求的url中,中間通過(guò)特定字符@隔開(kāi),然后我們拿到后進(jìn)行處理,
// 判斷是否為 XHR 請(qǐng)求 (通過(guò) URL 或 Accept 頭判斷) if ((url.contains("/api/") || acceptHeader?.contains("application/json") == true || headers["X-Requested-With"] == "XMLHttpRequest") && !url.endsWith("html")) { LogUtils.i( "攔截到 XHR 請(qǐng)求: $url") // 在此可以攔截或修改 XHR 請(qǐng)求 var respone: Response? = null if(method == "POST"){ val params = url.split("@") val test = params[0] // 獲取 POST 請(qǐng)求的 body 數(shù)據(jù) val postData = URLDecoder.decode(params[1], StandardCharsets.UTF_8.toString()) LogUtils.i("postData = $postData") respone = fetchApiData2(test,method,postData) }else if(method == "GET"){ respone = fetchApiData(url,method,null) } val byteStream = respone?.body()?.byteStream() ?: ByteArrayInputStream(ByteArray(0)) val contentType = respone?.header("Content-Type", "application/json") // 設(shè)置默認(rèn)內(nèi)容類(lèi)型 val contentLength = respone?.header("Content-Length", "-1")?.toLong() // 設(shè)置內(nèi)容長(zhǎng)度 LogUtils.i("fetchApiData respone = ${respone.toString()}") return WebResourceResponse( contentType, // 內(nèi)容類(lèi)型 "utf-8", // 編碼格式 200, // HTTP 狀態(tài)碼 "OK", // 狀態(tài)描述 mapOf( // 響應(yīng)頭 "Access-Control-Allow-Origin" to "*", "Content-Length" to contentLength.toString() ), byteStream // 響應(yīng)內(nèi)容的輸入流 ) // return WebResourceResponse("application/json", "utf-8",respone) }
private fun fetchApiData2(url: String, method: String, postData: String?): Response{ LogUtils.i("fetchApiData2 = $url + $method + $postData") val client = UnsafeOkHttpClient.unsafeOkHttpClient.build() val requestBody = RequestBody.create( MediaType.parse("application/json; charset=utf-8"), // 假設(shè)請(qǐng)求體是 JSON 數(shù)據(jù) postData ?: "" // POST 請(qǐng)求的數(shù)據(jù) ) val requestBuilder = Request.Builder() .url(url) .post(requestBody) .build() return client.newCall(requestBuilder).execute() }
// 根據(jù)請(qǐng)求類(lèi)型進(jìn)行處理 private fun fetchApiData(url: String, method: String, postData: Map<String, String>?): Response { LogUtils.i("fetchApiData = $url + $method + $postData") val client = UnsafeOkHttpClient.unsafeOkHttpClient.build() val requestBuilder = Request.Builder().url(url) // 根據(jù)請(qǐng)求類(lèi)型構(gòu)建請(qǐng)求 if (method == "POST" && postData != null) { val formBody = FormBody.Builder() postData.forEach { (key, value) -> formBody.add(key, value) } requestBuilder.post(formBody.build()) } val request = requestBuilder.build() return client.newCall(request).execute() // 直接返回 Response 對(duì)象 }
總結(jié)
到此這篇關(guān)于Android webview攔截H5的接口請(qǐng)求并返回處理好的數(shù)據(jù)的文章就介紹到這了,更多相關(guān)Android webview攔截H5接口請(qǐng)求內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android自定義控件深入學(xué)習(xí) Android生成隨機(jī)驗(yàn)證碼
這篇文章主要再次為大家介紹了Android自定義控件,以及針對(duì)自定義view學(xué)習(xí),實(shí)戰(zhàn)演練了Android生成隨機(jī)驗(yàn)證碼的詳細(xì)過(guò)程,感興趣的小伙伴們可以參考一下2016-01-01Android開(kāi)發(fā)MQTT協(xié)議的模型及通信淺析
這篇文章主要W為大家介紹了Android開(kāi)發(fā)MQTT協(xié)議的模型及通信淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Flutter中網(wǎng)絡(luò)圖片加載和緩存的實(shí)現(xiàn)
這篇文章主要介紹了Flutter中網(wǎng)絡(luò)圖片加載和緩存的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03Android RecyclerView使用入門(mén)介紹
RecyclerView是Android一個(gè)更強(qiáng)大的控件,其不僅可以實(shí)現(xiàn)和ListView同樣的效果,還有優(yōu)化了ListView中的各種不足。其可以實(shí)現(xiàn)數(shù)據(jù)縱向滾動(dòng),也可以實(shí)現(xiàn)橫向滾動(dòng)(ListView做不到橫向滾動(dòng))。接下來(lái)講解RecyclerView的用法2022-10-10Android實(shí)現(xiàn)界面內(nèi)嵌多種卡片視圖(ViewPager、RadioGroup)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)界面內(nèi)嵌多種卡片視圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09Android 圓角 ImageView類(lèi)可設(shè)置弧度(代碼簡(jiǎn)單)
這篇文章主要介紹了Android 圓角 ImageView類(lèi)可設(shè)置弧度 的相關(guān)資料,需要的朋友可以參考下2016-03-03Android開(kāi)發(fā)應(yīng)用第一步 安裝及配置模擬器Genymotion
這篇文章主要介紹了Android開(kāi)發(fā)應(yīng)用第一步,即安裝及配置模擬器Genymotion,感興趣的小伙伴們可以參考一下2015-12-12Android實(shí)現(xiàn)定時(shí)器的五種方法實(shí)例詳解
這篇文章主要介紹了Android實(shí)現(xiàn)定時(shí)器的五種方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02Android實(shí)現(xiàn)簡(jiǎn)單的分頁(yè)效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)單的分頁(yè)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Android 自定義密碼輸入框?qū)崿F(xiàn)代碼
最近做個(gè)項(xiàng)目自定義密碼輸入框功能,下面小編把實(shí)現(xiàn)思路分享到腳本之家平臺(tái),需要的朋友參考下吧2018-03-03