You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.22 01:45:04