Android WebView POST请求重定向无法被shouldOverrideUrlLoading捕获如何处理
解决方案
方案1:通过shouldInterceptRequest拦截所有请求(优先推荐,适配大多数场景)
Android WebView的shouldOverrideUrlLoading确实仅会拦截GET类型的页面跳转,POST表单提交、POST重定向都不会触发该回调。而shouldInterceptRequest会拦截WebView发起的所有资源请求(包含GET/POST等所有请求方法),可以在该回调内匹配目标支付重定向URL,拦截后自行携带自定义头发起请求,再把响应结果返回给WebView渲染。
修改你的WebView配置代码如下:
ui.webView.apply { settings.loadsImagesAutomatically = true settings.javaScriptEnabled = true settings.loadWithOverviewMode = true // 开启允许JS访问原生接口,方案2需要,仅用方案1可以不加 settings.javaScriptCanOpenWindowsAutomatically = true isVerticalScrollBarEnabled = true isHorizontalScrollBarEnabled = true scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY // 先注入JS交互接口,方案2需要,仅用方案1可以不加 addJavascriptInterface(object : Any() { @JavascriptInterface fun onFormSubmit(action: String, method: String, paramsJson: String) { if (method.equals("POST", ignoreCase = true) && action.startsWith("你的支付网关重定向URL前缀")) { // 此处可拿到表单提交的所有参数,自行构造POST请求,添加token和应用哈希头 // 请求拿到结果后调用webView.loadDataWithBaseURL加载结果即可 } } }, "android") webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading( view: WebView?, request: WebResourceRequest? ): Boolean { Timber.d("URL: ${request?.url}") //Just for debugging purpose return true } override fun shouldInterceptRequest( view: WebView?, request: WebResourceRequest? ): WebResourceResponse? { request ?: return super.shouldInterceptRequest(view, request) // 匹配目标POST重定向请求 if (request.method == "POST" && request.url.toString().startsWith("你的支付网关重定向URL前缀")) { return try { // 自行构造OkHttp请求,添加自定义头 val okRequest = Request.Builder() .url(request.url.toString()) .addHeader("Authorization", "Bearer 你的token") .addHeader("App-Static-Hash", "你的应用静态哈希值") // 携带原请求的所有头,避免缺失必要参数 .apply { request.requestHeaders.forEach { (k, v) -> addHeader(k, v) } } .build() val response = OkHttpClient().newCall(okRequest).execute() val contentType = response.header("Content-Type", "text/html;charset=utf-8") val mimeType = contentType!!.split(";")[0] val charset = contentType.split("charset=").getOrNull(1) ?: "utf-8" // 把自定义请求的结果返回给WebView渲染 WebResourceResponse(mimeType, charset, response.body?.byteStream()) } catch (e: Exception) { Timber.e(e, "自定义支付请求失败") super.shouldInterceptRequest(view, request) } } return super.shouldInterceptRequest(view, request) } // 页面加载完成后注入JS拦截表单提交,方案2需要,仅用方案1可以不加 override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) view?.evaluateJavascript(""" (function(){ document.querySelectorAll('form').forEach(form => { form.addEventListener('submit', e => { e.preventDefault(); const params = {}; new FormData(form).forEach((v,k) => params[k] = v); window.android.onFormSubmit(form.action, form.method, JSON.stringify(params)); }) }) })() """.trimIndent(), null) } } webChromeClient = object : WebChromeClient() { override fun onProgressChanged(view: WebView?, newProgress: Int) { ui.loading.isVisible = newProgress != 100 } override fun onReceivedTitle(view: WebView?, title: String?) { ui.title.text = title } } setOnKeyListener(View.OnKeyListener { _, keyCode, event -> if (keyCode == KeyEvent.KEYCODE_BACK && event.action === MotionEvent.ACTION_UP && canGoBack()) { goBack() return@OnKeyListener true } false }) }.loadUrl(args.url)
方案2:注入JavaScript拦截表单提交事件
如果shouldInterceptRequest无法拿到POST请求的表单参数,可以通过注入JS的方式监听表单提交事件,拦截后把提交地址、参数都传给原生层,原生层自行发起带自定义头的请求,再把结果加载到WebView中。
该方案可控性更高,不会误拦截其他请求,适配Android全版本。
注意事项
shouldInterceptRequest运行在子线程,不要在回调内操作UI,网络请求可以直接在回调内同步执行。- 如果目标支付页面配置了内容安全策略(CSP)禁止内联JS执行,需要和支付网关侧沟通放开限制,否则注入的JS不会生效。
- 自定义请求返回响应给WebView时,注意正确填写MIME类型和编码,避免页面乱码。
内容的提问来源于stack exchange,提问作者Ayxan Vəliyev
相关产品推荐
相关产品推荐

