Flutter WebView中如何拦截请求后修改原请求的响应体(而非直接返回自定义响应)
Flutter WebView中如何拦截请求后修改原请求的响应体(而非直接返回自定义响应)
我完全懂你的需求——你不想直接返回一个自定义响应来“冒充”服务器内容,而是希望让原请求正常完成,拿到真实的服务器响应后,再对内容做修改,最后交给WebView渲染。刚好我之前也遇到过类似场景,用InAppWebView是可以实现的,下面给你具体方案:
核心思路:手动发起原请求,修改响应后返回
shouldInterceptRequest的设计就是让你接管请求处理流程,所以我们可以在这里手动发起和原请求完全一致的请求,拿到真实响应后修改内容,再封装成WebResourceResponse返回给WebView,这样就实现了“拦截后修改原响应”的效果。
具体代码实现
首先可以引入Dart自带的http包来处理请求,下面是完整的示例代码:
import 'package:http/http.dart' as http; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; // ... 你的其他页面代码 ... shouldInterceptRequest: (controller, request) async { // 只针对目标txt文件请求做处理 if (request.url.rawValue.toString().endsWith(".txt")) { try { // 1. 复刻原请求的所有参数:URL、请求方法、Headers、Body final uri = request.url.toUri(); http.BaseRequest httpRequest; // 根据原请求方法创建对应HTTP请求 switch (request.method) { case "GET": httpRequest = http.Request('GET', uri); break; case "POST": httpRequest = http.Request('POST', uri); // 处理POST请求的body if (request.body != null) { (httpRequest as http.Request).bodyBytes = request.body!; } break; // 可根据需求扩展PUT、DELETE等其他方法 default: httpRequest = http.Request(request.method, uri); } // 复制原请求的所有请求头 request.headers.forEach((key, value) { httpRequest.headers[key] = value; }); // 2. 发起请求,拿到服务器真实响应 final httpResponse = await http.Client().send(httpRequest); final responseBytes = await httpResponse.stream.toBytes(); final responseBody = String.fromCharCodes(responseBytes); // 3. 自定义修改响应内容的逻辑 final modifiedBody = responseBody.replaceAll("原始文本内容", "替换后的自定义内容"); final modifiedBytes = modifiedBody.codeUnits; // 4. 封装成WebView可识别的响应对象返回 return WebResourceResponse( data: modifiedBytes, mimeType: httpResponse.headers['content-type'] ?? 'text/plain', encoding: httpResponse.headers['content-encoding'] ?? 'utf-8', statusCode: httpResponse.statusCode, reasonPhrase: httpResponse.reasonPhrase, headers: httpResponse.headers.map((key, value) => MapEntry(key, value.join(','))), ); } catch (e) { // 请求出错时,返回null让WebView自动处理原请求 return null; } } // 非目标请求直接放行,交给WebView正常处理 return null; }
补充:JavaScript注入方案(可选)
如果你更倾向用JS来修改响应,也可以尝试在页面加载时注入脚本,拦截fetch或XMLHttpRequest的响应:
onWebViewCreated: (controller) { webViewController = controller; // 注入本地JS文件拦截响应 controller.injectJavaScriptFileFromAsset(assetFilePath: 'assets/intercept_response.js'); }
然后在assets/intercept_response.js中编写拦截逻辑:
// 拦截fetch请求 const originalFetch = window.fetch; window.fetch = async (...args) => { const response = await originalFetch(...args); if (args[0].endsWith('.txt')) { const text = await response.text(); const modifiedText = text.replaceAll("原始文本", "修改后的文本"); // 返回修改后的新响应 return new Response(modifiedText, { status: response.status, statusText: response.statusText, headers: response.headers, }); } return response; }; // 拦截XMLHttpRequest const originalOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url) { originalOpen.call(this, method, url); if (url.endsWith('.txt')) { const originalOnLoad = this.onload; this.onload = function() { this.responseText = this.responseText.replaceAll("原始文本", "修改后的文本"); if (originalOnLoad) originalOnLoad.call(this); }; } };
不过这种方式调试起来确实麻烦,而且如果页面有CSP限制或者自定义了请求封装,可能会导致拦截失效,所以还是前面的shouldInterceptRequest方案更稳定可靠。
内容来源于stack exchange
相关产品推荐
相关产品推荐

