Xamarin Forms WebView:基础认证实现与导航事件失效问题排查
在Xamarin Forms项目中使用Hybrid Renderer和WebView,需要向页面注入JavaScript代码。主项目包含CustomWebView.cs,用于处理导航事件(如拨号、文件下载跳转);Android项目包含HybridWebViewRenderer.cs。原本功能正常,为实现基础认证,在HybridWebViewRenderer中添加了自定义AuthWebViewClient,认证功能生效后,WebViewNavigating事件仅触发一次,后续不再执行。请问:
- 能否不使用自定义WebViewClient实现基础认证?
- 如何在使用自定义客户端的同时保留CustomWebView的原有功能?
相关代码
主项目CustomWebView.cs
namespace ClotureSiadForms.Renderer { public class CustomWebView : WebView { public string js = ""; public CustomWebView() { Navigating+= WebViewNavigating; Navigated+=WebViewNavigated; } private void WebViewNavigated(object sender, WebNavigatedEventArgs args) { EvaluateJavaScriptAsync(js); } public void WebViewNavigating(object sender, WebNavigatingEventArgs args) { if (args.Url.StartsWith("tel:")) { var tel = args.Url.Split(':')[1]; args.Cancel = true; Xamarin.Essentials.PhoneDialer.Open(tel); } else if (!args.Url.StartsWith("http") || args.Url.EndsWith(".apk") || args.Url.EndsWith(".pdf") || args.Url.EndsWith(".zip")) { args.Cancel = true; Xamarin.Essentials.Launcher.OpenAsync(args.Url); } } } }
Android项目原有HybridWebViewRenderer.cs
[assembly: ExportRenderer(typeof(CustomWebView), typeof(HybridWebViewRenderer))] namespace ClotureSiadForms.Droid.Renderer { internal class HybridWebViewRenderer : WebViewRenderer { public HybridWebViewRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e) { base.OnElementChanged(e); if (Control != null) { CustomWebView webview = e.NewElement as CustomWebView; Control.Settings.JavaScriptEnabled = true; Control.Settings.DomStorageEnabled = true; Control.Settings.SavePassword = true; } } } }
添加认证后的HybridWebViewRenderer.cs
[assembly: ExportRenderer(typeof(CustomWebView), typeof(HybridWebViewRenderer))] namespace ClotureSiadForms.Droid.Renderer { internal class HybridWebViewRenderer : WebViewRenderer { public HybridWebViewRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e) { base.OnElementChanged(e); if (Control != null) { CustomWebView webview = e.NewElement as CustomWebView; Control.Settings.JavaScriptEnabled = true; Control.Settings.DomStorageEnabled = true; Control.Settings.SavePassword = true; var login = Preferences.Get("login", ""); var password = Preferences.Get("password", ""); Control.SetWebViewClient(new AuthWebViewClient(login, password)); } } public class AuthWebViewClient : WebViewClient { private string Username; private string Password; public AuthWebViewClient(string username, string password) { Username = username; Password = password; } public override void OnReceivedHttpAuthRequest(Android.Webkit.WebView view, HttpAuthHandler handler, string host, string realm) { handler.Proceed( Username,Password); } } } }
解决方案
一、不使用自定义WebViewClient实现基础认证
可以通过在请求URL中直接拼接基础认证信息的方式实现,格式为https://用户名:密码@目标域名。示例代码如下:
// 在CustomWebView加载URL时拼接认证信息 string login = Preferences.Get("login", ""); string password = Preferences.Get("password", ""); string targetUrl = "https://your-target-domain.com"; // 对用户名密码进行URL编码,避免特殊字符导致错误 string encodedLogin = Uri.EscapeDataString(login); string encodedPassword = Uri.EscapeDataString(password); Uri targetUri = new Uri(targetUrl); string authUrl = $"https://{encodedLogin}:{encodedPassword}@{targetUri.Host}{targetUri.PathAndQuery}"; // 给CustomWebView设置带认证的URL CustomWebView webView = new CustomWebView(); webView.Source = new UrlWebViewSource { Url = authUrl };
注意:这种方式受部分WebView安全策略限制(比如Chrome的部分版本会屏蔽明文携带认证信息的URL),需要针对目标设备做兼容性测试。
二、使用自定义WebViewClient同时保留原有功能
问题核心是你自定义的AuthWebViewClient继承了原生WebViewClient,替换了Xamarin Forms默认的客户端,导致导航事件无法转发到Forms层的WebViewNavigating和WebViewNavigated。正确做法是继承Xamarin Forms提供的FormsWebViewClient,而非原生WebViewClient,这样既能保留认证逻辑,又能保证Forms层事件正常触发。
修改后的Android项目HybridWebViewRenderer.cs代码:
[assembly: ExportRenderer(typeof(CustomWebView), typeof(HybridWebViewRenderer))] namespace ClotureSiadForms.Droid.Renderer { internal class HybridWebViewRenderer : WebViewRenderer { public HybridWebViewRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e) { base.OnElementChanged(e); if (Control != null) { CustomWebView webview = e.NewElement as CustomWebView; Control.Settings.JavaScriptEnabled = true; Control.Settings.DomStorageEnabled = true; Control.Settings.SavePassword = true; var login = Preferences.Get("login", ""); var password = Preferences.Get("password", ""); // 传入当前Renderer实例,保证事件转发 Control.SetWebViewClient(new AuthWebViewClient(login, password, this)); } } // 继承FormsWebViewClient而非原生WebViewClient public class AuthWebViewClient : FormsWebViewClient { private string Username; private string Password; public AuthWebViewClient(string username, string password, WebViewRenderer renderer) : base(renderer) { Username = username; Password = password; } public override void OnReceivedHttpAuthRequest(Android.Webkit.WebView view, HttpAuthHandler handler, string host, string realm) { handler.Proceed(Username, Password); } // 如需额外处理导航逻辑,重写方法时必须调用base方法,保证Forms层事件触发 public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request) { // 先让FormsWebViewClient处理基础导航逻辑 var result = base.ShouldOverrideUrlLoading(view, request); // 可在此添加自定义原生导航逻辑 return result; } } } }
修改后,自定义认证功能正常生效的同时,Forms层的WebViewNavigating和WebViewNavigated事件会持续触发,原有拨号、下载跳转的功能也能完整保留。
内容的提问来源于stack exchange,提问作者Entretoize

