这个文档指出它无法完成,所以我正在寻找替代解决方案:
目前,Android 不支持在使用 WebViewController 的 loadRequest 方法发出 POST 请求时设置自定义标头。如果您需要此功能,解决方法是手动发出请求,然后使用 loadHtmlString 加载响应数据。
他们提供的解决方法也有其自身的问题。 loadHtmlString 方法仅在 webview 元素中显示无样式、无响应的页面,并出现以下控制台错误:
I/chromium(19539): [INFO:CONSOLE(286)] "Uncaught ReferenceError: $ is not defined", source: about:blank (286)
I/chromium(19539): [INFO:CONSOLE(636)] "Uncaught ReferenceError: jQuery is not defined", source: about:blank (636)
这是我的 loadHtmlString 版本的实现。我尝试加载 jQuery 来解决无响应问题,但我没有与被忽略的样式表相关的消息:
class _PortalWebviewState extends State<PortalWebview> {
late final WebViewController controller;
@override
void initState() {
super.initState();
controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted);
_loadRequestWithAuthHeader();
controller.addJavaScriptChannel(
'Flutter',
onMessageReceived: (JavaScriptMessage message) {
print('JavaScript Error: ${message.message}');
},
);
}
Future<void> injectJQuery() async {
await controller.runJavaScript('''
var script = document.createElement('script');
script.src = 'https://code.jquery.com/jquery-3.6.0.min.js';
script.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(script);
''');
}
Future<void> _loadRequestWithAuthHeader() async {
const storage = FlutterSecureStorage();
final token = await storage.read(key: 'token');
if (token != null) {
print("Token found in keychain.");
final url = Uri.parse('${Config.domain}/mobile_index.php');
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
};
try {
final response = await http.post(url, headers: headers);
print('Portal response status code: ${response.statusCode}');
if (response.statusCode == 200) {
await injectJQuery();
await controller.loadHtmlString(response.body);
} else {
print('Error: ${response.statusCode}');
// Handle error cases here
}
} catch (e) {
print('Error: $e');
// Handle exceptions here
}
} else {
print('Token not found');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: const Text('COMPANY NAME'),
actions: [
NavigationControls(controller: controller),
// Menu(controller: controller),
],
),
body: PortalWebviewStack(controller: controller),
);
}
它返回无样式的 HTML,其中包含加载空白页面的损坏链接 - 我的服务器日志不显示单击 Web 视图中的链接后收到任何请求。
我相信将 auth 标头作为 GET 请求的一部分发送可能是一个更简单的解决方案,但我喜欢 POST 的安全性,并且相信这种解决方法在我的用例中应该是可能的,因为它在上面的文档和 this 中被引用Github问题。
我决定使用 LoadRequestMethod.get 来跳过 Android 自定义 POST 标头限制:
class _InvoiceWebviewState extends State<InvoiceWebview> {
late final WebViewController controller;
@override
void initState() {
super.initState();
controller = WebViewController();
_loadRequestWithAuthHeader();
}
Future<void> _loadRequestWithAuthHeader() async {
const storage = FlutterSecureStorage();
final token = await storage.read(key: 'token');
if (token != null) {
final headers = {'Authorization': 'Bearer $token'};
await controller.loadRequest(
Uri.parse('${Config.domain}/invoices/new'),
method: LoadRequestMethod.get,
headers: headers,
);
} else {
print('Token not found');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: const Text('COMPANY NAME'),
actions: [
NavigationControls(controller: controller),
// Menu(controller: controller),
],
),
body: InvoiceWebviewStack(controller: controller),
);
}
}
我放弃了自定义 POST 标头,因为使用
loadHtmlString
需要重新格式化我的 CSS 设置并解决 jQuery 问题。相反,我在 GET 请求中发送授权标头。标头在查询字符串中不可见,我仍然受益于 https 加密,最重要的是,它适用于 iOS 和 Android。