Android webview intercepts H5's interface request and returns processed data
Android can passWebView
ofshouldInterceptRequest
Method intercepts network requests in H5. This is oneWebViewClient
The callback method in allows developers to process and modify WebView when it initiates a network request.
The specific usage method is as follows:
You need to create a custom one
WebViewClient
, and rewrittenshouldInterceptRequest
method.In this method, you can intercept web requests initiated by the WebView and return a custom response, or let the request continue.
-
This method introduces two overloaded methods in API 21 (Android 5.0) and later:
-
shouldInterceptRequest(WebView view, String url)
(API 11) -
shouldInterceptRequest(WebView view, WebResourceRequest request)
(API 21)
-
Here is a code example:
// Kotlin code example = object : WebViewClient() { // For API 21 and later override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = () // Here you can judge the URL and intercept or modify the request as needed if (("your_target_url")) { // Some processing can be done here, such as replacing the request or returning local data val inputStream = ... // Custom input stream return WebResourceResponse("text/html", "UTF-8", inputStream) } // Don't intercept, continue to request return (view, request) } // For API 11 to API 20 devices override fun shouldInterceptRequest( view: WebView, url: String ): WebResourceResponse? { // The processing logic here is similar to the above code if (("your_target_url")) { // Custom processing logic val inputStream = ... return WebResourceResponse("text/html", "UTF-8", inputStream) } return (view, url) } }
existshouldInterceptRequest
In the method, you can return aWebResourceResponse
Object to change or replace the original network request, and the request can also be continued through the default implementation.
Get the data requested by the network interface (such as the response of the API request) and return it to H5.
There are several ways to consider:
1. Intercept the request and initiate the request manually
You can passshouldInterceptRequest
Methods intercept WebView's API requests and use them in Java or Kotlin.HttpURLConnection
orOkHttp
Start a network request, and after processing the response, pass the response data to H5.
Sample code:
= object : WebViewClient() { override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = () // Determine whether it is an interface request that needs to be intercepted if (("your_api_endpoint")) { //Stamp the request via OkHttp or HttpURLConnection val response = fetchApiData(url) // Pass the retrieved response content to H5 { ("javascript:handleApiResponse('${response}')", null) } // Return an empty response or custom content return WebResourceResponse("application/json", "UTF-8", null) } return (view, request) } // Use OkHttp to initiate network requests (you can choose the appropriate network library according to your needs) private fun fetchApiData(url: String): String { // Simple OkHttp request example val client = OkHttpClient() val request = ().url(url).build() (request).execute().use { response -> return ?.string() ?: "" } } }
In this example,shouldInterceptRequest
It will intercept the specified network request and then useOkHttp
Start a request and pass the obtained response data throughevaluateJavascript
The method is passed back to the H5 page in the WebView.
2. JavaScript communication through WebView and H5
Another way is through WebViewaddJavascriptInterface
Method, communicate Android code with H5. When Android initiates a request, data can be passed to H5 through the interface.
Define a JavaScript function in H5 to receive data:
function handleApiResponse(response) { ("API Response: ", response); // Here you can process the response, render to the page, etc.}
On the Android side:
(object { @JavascriptInterface fun sendDataToH5(data: String) { // Send data to H5 ("javascript:handleApiResponse('$data')", null) } }, "AndroidInterface") = object : WebViewClient() { override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = () if (("your_api_endpoint")) { val response = fetchApiData(url) // Pass data to H5 through JavaScript interface { ("javascript:AndroidInterface.sendDataToH5('${response}')", null) } return WebResourceResponse("application/json", "UTF-8", null) } return (view, request) } }
Does this need to take into account the type of network interface request sent by H5? Post or get
Yes, in useshouldInterceptRequest
When intercepting network requests sent by H5,The type of request (GET or POST) needs to be considered. This is because GET and POST requests are handled differently, especially POST requests, which carry body data, while GET requests usually only contain URL parameters.
shouldInterceptRequest
It can help you intercept requests, but when handling POST requests, special attention should be paid to extracting and using the request body.
How to distinguish between GET and POST requests?
In API 21 and later,shouldInterceptRequest
The method ofWebResourceRequest
The object contains more information, and can be used through itsgetMethod()
Methods to distinguish request types.
override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = () val method = // Get request method such as GET or POST // Process according to the request type if (method == "POST") { // Get the body requested by POST (in Android WebView, it is not supported to directly obtain the POST body unless a specific tool is used) // Custom logic processing POST requests } else if (method == "GET") { // Process GET requests } return (view, request) }
Processing POST requests
Android'sshouldInterceptRequest
It does not directly provide the function of obtaining the POST request body. However, you can use other ways to handle it, such as passing the request body to Android in advance in JS.
Method 1: Use evaluateJavascript to pass POST request data
You can have the H5 page send the POST requested data to the WebView in advance through JavaScript before making a POST request. The Android side can then intercept and process this data.
-
Intercept and pass POST request data in H5:
function interceptAndSendPostData(url, data) { // Call the Android interface to pass data to WebView (url, (data)); // Continue to initiate POST request fetch(url, { method: "POST", body: (data), headers: { "Content-Type": "application/json" } }).then(response => ()) .then(data => (data)); }
-
Receive POST data in Android:
(object { @JavascriptInterface fun sendPostData(url: String, data: String) { // Here you can process the passed POST request data ("WebView", "POST request URL: $url, data: $data") } }, "AndroidInterface")
Method 2: Send POST requests through custom network layer
Another way is toshouldInterceptRequest
After intercepting the POST request, the HTTP request is manually initiated to process the request body and response data. AlthoughshouldInterceptRequest
The request body is not provided directly, but you can pass the POST request body to Android in advance through JavaScript in the H5 page, or handle it through other network interception methods (such as using OkHttp's interceptor).
override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = () val method = // Get request method such as GET or POST if (method == "POST") { // Customize the HTTP request to process POST request body and response val response = performCustomHttpPost(url, request) return WebResourceResponse("application/json", "UTF-8", response) } return (view, request) } fun performCustomHttpPost(url: String, request: WebResourceRequest): InputStream { // Send custom POST requests using OkHttp or HttpURLConnection val client = OkHttpClient() val requestBody = ... // Build the request body val request = () .url(url) .post(requestBody) .build() val response = (request).execute() return ?.byteStream() ?: ByteArrayInputStream(ByteArray(0)) }
summary
- GET Request: You can intercept and process directly through URL and request header information.
- POST request: Special processing of request volume data is required. You can pass POST request data to the WebView in advance through JavaScript, or process POST requests and their responses by manually initiating HTTP requests.
Can InterceptRequest determine the type of network request? For example, script xhr, etc.
shouldInterceptRequest
It cannot directly determine the specific type of network request (such asscript
、xhr
, etc.), because it does not provide a field that explicitly indicates that the request is a resource of some type (such as JavaScript files, XHR requests, etc.). However, you can request itURLandRequest header informationTo infer the request type.
In Android versions of API 21 and above,WebResourceRequest
The object provides rich information, including the requested URL, request method (GET, POST, etc.), request header, etc., and the request type can be inferred based on this information.
How to determine the request type by URL and request header?
-
By URL suffix:
- If the requested URL is
.js
At the end, it can usually be considered as ascript
ask. - If the URL contains
/api/
or similar identifiers, which may bexhr
ask. - CSS usually
.css
At the end, the picture file is.jpg
、.png
、.gif
Ending, etc.
- If the requested URL is
-
Pass request header: You can infer the request type by the request header, for example
Accept
The header usually indicates the type of data the client expects to receive.-
Accept: application/json
Commonly used inxhr
ask. -
Accept: text/css
Indicates the request for a CSS file. -
Accept: application/javascript
orAccept: text/javascript
Used for JavaScript file requests.
-
Code example: How to infer a request type by URL and request header
override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = () val headers = val acceptHeader = headers["Accept"] // Get the Accept header // Determine whether it is a script request (script) if ((".js") || acceptHeader?.contains("application/javascript") == true) { ("WebView", "Intercept script request: $url") // You can intercept or modify script requests here } // Determine whether it is an XHR request (judged by URL or Accept header) if (("/api/") || acceptHeader?.contains("application/json") == true) { ("WebView", "Intercepted XHR request: $url") // Here you can intercept or modify XHR requests } // Determine whether it is a CSS request if ((".css") || acceptHeader?.contains("text/css") == true) { ("WebView", "Intercepted CSS request: $url") // CSS requests can be intercepted or modified here } // Requests for other resource types ("WebView", "Other types of requests: $url") return (view, request) }
How to judge common resource types
-
JavaScript file request (
script
):- Judging by URL:
(".js")
- pass
Accept
Head judgment:application/javascript
ortext/javascript
- Judging by URL:
-
XHR Request:
- Judging by URL rules: If the URL contains
/api/
、/ajax/
or other common API paths. - pass
Accept
Head judgment:application/json
- Judging by URL rules: If the URL contains
-
CSS file request:
- Judging by URL:
(".css")
- pass
Accept
Head judgment:text/css
- Judging by URL:
-
Image file request:
- Judging by URL:
(".jpg")
、.png
、.gif
wait - pass
Accept
Head judgment:image/*
- Judging by URL:
-
HTML page request:
- pass
Accept
Head judgment:text/html
- pass
summary
AlthoughshouldInterceptRequest
There is no field directly provided for resource type, but you can use the URL format and request header (especiallyAccept
header) to infer the request type and thus intercept specific resource requests.
What are script xhr font png preflight and how should we judge? Can we judge them by val acceptHeader = headers["Accept"]?
Accept
The header indicates the type of response data the client wants to receive in an HTTP request. It can indeed help us determine the type of resource requested in some cases. But not all types of requests will be used explicitly.Accept
head. Therefore, we need to combineURLandRequest headerOther information (such asContent-Type
、Access-Control-Request-Method
etc.) to make judgments.
1. Script (JavaScript Request)
JavaScript requests are used to load.js
File resource request.
Judgment basis:
-
URL suffix: Usually
.js
File suffix. -
Accept
head:application/javascript
ortext/javascript
。
if ((".js") || acceptHeader?.contains("application/javascript") == true || acceptHeader?.contains("text/javascript") == true) { ("WebView", "Intercepted JavaScript request: $url") }
2. XHR request (XMLHttpRequest/Fetch request)
XHR requests are usually used for AJAX requests or useFetch API
asynchronous network requests.
Judgment basis:
-
URL Rules: API requests usually have specific paths, such as
/api/
、/ajax/
wait. -
Accept
head:application/json
(If JSON data is returned). -
Request header:XHR request will carry
X-Requested-With: XMLHttpRequest
Head (but not all cases have this head).
if (("/api/") || acceptHeader?.contains("application/json") == true || headers["X-Requested-With"] == "XMLHttpRequest") { ("WebView", "Intercepted XHR request: $url") }
3. Font request (font file request)
Font files are usually.woff
、.woff2
、.ttf
、.otf
Even the end of the suffix.
Judgment basis:
-
URL suffix:
.woff
、.woff2
、.ttf
、.otf
。 -
Accept
head:font/*
orapplication/font-woff
。
if ((".woff") || (".woff2") || (".ttf") || (".otf") || acceptHeader?.contains("font/") == true) { ("WebView", "Intercepted font file request: $url") }
4. PNG request (picture request)
Image requests usually include.png
、.jpg
、.gif
etc.
Judgment basis:
-
URL suffix: Picture files are usually
.png
、.jpg
、.jpeg
、.gif
End. -
Accept
head:image/*
。
if ((".png") || (".jpg") || (".jpeg") || (".gif") || acceptHeader?.contains("image/") == true) { ("WebView", "Intercepted image request: $url") }
5. Preflight request
A Preflight request is part of a CORS request and is usually sent before a cross-domain POST, PUT, DELETE request to determine whether the server allows the request. Preflight request to useOPTIONS
Method initiated.
Judgment basis:
-
HTTP Methods:
OPTIONS
(Preflight request isOPTIONS
Request of method). -
Request header:Include
Access-Control-Request-Method
andAccess-Control-Request-Headers
。
if ( == "OPTIONS" && ("Access-Control-Request-Method")) { ("WebView", "Intercept Preflight request: $url") }
Combine the URL and Accept header for judgment
Summarize the basis for judging different request types:
Request Type | URL suffix |
Accept head |
Other request header information |
---|---|---|---|
Script (JavaScript) | .js |
application/javascript , text/javascript
|
|
XHR (AJAX) |
/api/ , /ajax/
|
application/json |
X-Requested-With: XMLHttpRequest |
Font (font) |
.woff , .woff2 , .ttf , .otf
|
font/* , application/font-woff
|
|
Image (Picture) |
.png , .jpg , .jpeg , .gif
|
image/* |
|
Preflight (CORS) |
OPTIONS method,Access-Control-Request-Method
|
summary
-
Accept
The header can help determine the request type, but not all requests use it explicitly. - According to URL suffix, request method, other request headers (such as
X-Requested-With
、Access-Control-Request-Method
) Combining judgment can more accurately identify different types of requests.
How to deal with the response returned does not meet the expected format
AppearAxiosError: Network Error, Code: ERR_NETWORK
Errors are usually because the response returned by the WebView does not meet the expected format when processing intercepted network requests, or some necessary header information is missing. Here are a few common reasons and solutions:
Possible causes of the problem:
Response stream format or processing error: You are returning
()?.byteStream()
, but you still need to ensure the wholeWebResourceResponse
Objects are correctly configured, especially content type, encoding format and response header information. Any error can cause H5 (for example through Axios) to think that the network request has failed.Missing or incomplete response header:
WebResourceResponse
Some key response headers need to be provided, such asContent-Type
andContent-Length
, These headers are required in network request processing. Axios requested by H5 need to know the length and type of the response, otherwise the response will be considered invalid.The returned stream is incomplete or closed in advance: If there is a problem with the returned stream, such as it is closed in advance or has other exceptions, it may cause Axios to report an error during processing.
How to correctly return a custom response
Make sure you useWebResourceResponse
When building the response, all necessary header information and stream data are included.
Modify code example
override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = () if (("your_api_endpoint")) { // Initiate a network request to obtain data val response = fetchApiData(url) // Process the returned response stream val byteStream = ?.byteStream() ?: ByteArrayInputStream(ByteArray(0)) val contentType = ("Content-Type", "application/json") // Set the default content type val contentLength = ("Content-Length", "-1").toLong() // Set content length // Build WebResourceResponse and return it to WebView return WebResourceResponse( contentType, // Content Type "utf-8", // Encoding format 200, // HTTP status code "OK", // Status Description mapOf( // Response header "Access-Control-Allow-Origin" to "*", "Content-Length" to () ), byteStream // Respond to the input stream of content ) } return (view, request) } // Use OkHttp to initiate a network requestprivate fun fetchApiData(url: String): Response { val client = OkHttpClient() val request = ().url(url).build() return (request).execute() // Return the Response object directly}
Key modification points:
Set content type and encoding correctly:pass
("Content-Type")
Get the original responseContent-Type
, and pass toWebResourceResponse
. Usuallyapplication/json
。set up
Content-Length
:pass("Content-Length")
Get the length of the response content and pass it to the WebView. If missingContent-Length
, some H5 request libraries (such as Axios) may report errors.Returns a valid response stream: You need to make sure
()?.byteStream()
It is effective. If the network request fails or the stream is empty, you can return an emptyByteArrayInputStream()
。Set status code and status description: If the request is successful, make sure to return
200 OK
, otherwise the corresponding status code and description should be returned according to the specific situation.
Checkpoint
Ensure that the response flow is effective:make sure
()?.byteStream()
There is really data, otherwise the H5 side will consider it an invalid response. You can view the status of the response flow through debugging.Correctly set header information:in particular
Content-Type
andContent-Length
, they are the key to network request processing. Axios relies on these headers to parse the response.Error handling: If an error occurs during the network request process, an appropriate error response should be returned, such as
404 Not Found
or500 Internal Server Error
, instead of returning an empty response stream directly.
Debugging suggestions
- Through log debugging, output the status of the OkHttp request, ensure
fetchApiData
The function returnsResponse
The object contains valid data. - Check the returned response header on the Axios side to ensure
Content-Type
、Content-Length
etc. information is correct.
shouldInterceptRequest When intercepting POST requests, how to obtain the body data of the POST request?
On AndroidWebViewClient
PassedshouldInterceptRequest
When intercepting POST requests and building a custom HTTP request, you first need to manually build the request body (such as fromWebResourceRequest
Get the necessary request information in ), and then useOkHttp
orHttpURLConnection
Send this POST request.
WebResourceRequest
The POST request body is not provided directly, so the POST request body needs to be obtained and manually constructed. This can be passedrequestHeaders
To build, or pass POST data in advance when sent on the front end (H5).
A complete example of processing a POST request and building a request body:
override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest ): WebResourceResponse? { val url = () val method = if (method == "POST") { ("WebView", "Intercepted POST request: $url") // Customize the processing of POST requests, build the request body and send it val responseStream = performCustomHttpPost(url, request) // Returns a custom WebResourceResponse return WebResourceResponse( "application/json", // Assume that the return is a JSON response "UTF-8", 200, // HTTP status code "OK", // HTTP status description mapOf("Access-Control-Allow-Origin" to "*"), // Response header responseStream // Input stream for response data ) } return (view, request) } // Customize the logic of processing POST requestsfun performCustomHttpPost(url: String, request: WebResourceRequest): InputStream { // Build a POST request body (assuming that the POST data sent by the H5 side is in JSON format) val postData = getPostData(request) // Assume that the request volume data can be extracted here // Logging request volume data ("WebView", "POST request data: $postData") // Use OkHttp to send a custom POST request val client = OkHttpClient() val requestBody = ( ("application/json; charset=utf-8"), // Assume that the request body is JSON data postData ?: "" // POST requested data ) val customRequest = () .url(url) .post(requestBody) .build() // Initiate a request and return the response stream val response = (customRequest).execute() ("WebView", "HTTP Response code: ${()}") // Logging response status code return ?.byteStream() ?: ByteArrayInputStream(ByteArray(0)) } // Get POST data (need to be obtained through front-end or other means)fun getPostData(request: WebResourceRequest): String? { // WebView cannot directly obtain POST body data, and the front-end needs to cooperate to pass it through evaluateJavascript or other methods. // Suppose we get some data from requestHeaders (actually, it can be modified according to your needs) val contentType = ["Content-Type"] ("WebView", "Content-Type: $contentType") // Here we return the simulated POST data, which needs to be processed in actual situations. return "{ \"key\": \"value\" }" }
Key points: Get request body data:Android WebView No access provided by itself POST Direct way to request body,Need to cooperate through front-end(like evaluateJavascript)To pass the request body,Or process the simulation request body by yourself。 Manually build POST Request body:use OkHttp of () 方法Manually build POST Request body。 Logging: pass () Record拦截of URL 和Request body数据,Easy to debug。 Record HTTP Response status code,Check whether the request is successful。 Handle exceptions:like果响应体为空,返回一个空of ByteArrayInputStream(),To ensure WebView Will not crash。 关于获取真实of POST Request body Android of WebView Get it directly without a built-in mechanism POST Request body,因此需要pass JavaScript and Android Communication,exist H5 Actively POST Data sent to WebView。例like:
// Get the POST request body on the H5 side and pass it to Androidfunction sendPostDataToAndroid(data) { if () { ((data)); } }
Then pass on the Android sideaddJavascriptInterface
Receive data:
@JavascriptInterface fun sendPostData(data: String) { // Process POST data passed from H5 ("WebView", "Received POST data: $data") }
at last
In fact, you can try the above method to obtain the post request body parameter content. Of course, there is also a simpler method to take advantage of it. If the number of H5 POST requests is not large, you can communicate with H5 and directly put the request data in the requested url, separate it with specific characters @ in the middle, and then we will process it after getting it.
// Determine whether it is an XHR request (judged by URL or Accept header) if ((("/api/") || acceptHeader?.contains("application/json") == true || headers["X-Requested-With"] == "XMLHttpRequest") && !("html")) { ( "Intercepted XHR request: $url") // Here you can intercept or modify XHR requests var respone: Response? = null if(method == "POST"){ val params = ("@") val test = params[0] // Get the body data requested by POST val postData = (params[1], StandardCharsets.UTF_8.toString()) ("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") // Set the default content type val contentLength = respone?.header("Content-Length", "-1")?.toLong() // Set content length ("fetchApiData respone = ${()}") return WebResourceResponse( contentType, // Content Type "utf-8", // Encoding format 200, // HTTP status code "OK", // Status Description mapOf( // Response header "Access-Control-Allow-Origin" to "*", "Content-Length" to () ), byteStream // Respond to the input stream of content ) // return WebResourceResponse("application/json", "utf-8",respone) }
private fun fetchApiData2(url: String, method: String, postData: String?): Response{ ("fetchApiData2 = $url + $method + $postData") val client = () val requestBody = ( ("application/json; charset=utf-8"), // Assume that the request body is JSON data postData ?: "" // POST requested data ) val requestBuilder = () .url(url) .post(requestBody) .build() return (requestBuilder).execute() }
// Process according to the request type private fun fetchApiData(url: String, method: String, postData: Map<String, String>?): Response { ("fetchApiData = $url + $method + $postData") val client = () val requestBuilder = ().url(url) //Build a request according to the request type if (method == "POST" && postData != null) { val formBody = () { (key, value) -> (key, value) } (()) } val request = () return (request).execute() // Return the Response object directly}
Summarize
This is the article about Android webview intercepting H5 interface requests and returning processed data. For more information about Android webview intercepting H5 interface requests, please search for my previous articles or continue browsing the following related articles. I hope everyone will support me in the future!