从虚拟机调用本地API报各种 SSL 连接不上的错误,这给本地调试造成了极大的不便,在被这个问题困扰了多日以后,终于在GitHub上找到答案
基于这个 帖子 ,有一个回复
他写了一个帮助类,专门用来调试本地的API,并给出了这个调用类的用法,此帮助类如下
using System.Net.Security;
public class DevHttpsConnectionHelper
{
public DevHttpsConnectionHelper(int sslPort)
{
SslPort = sslPort;
DevServerRootUrl = FormattableString.Invariant($"https://{DevServerName}:{SslPort}");
LazyHttpClient = new Lazy<HttpClient>(() => new HttpClient(GetPlatformMessageHandler()));
}
public int SslPort { get; }
public string DevServerName =>
#if WINDOWS
"localhost";
#elif ANDROID
"10.0.2.2";
#else
throw new PlatformNotSupportedException("Only Windows and Android currently supported.");
#endif
public string DevServerRootUrl { get; }
private Lazy<HttpClient> LazyHttpClient;
public HttpClient HttpClient => LazyHttpClient.Value;
public HttpMessageHandler? GetPlatformMessageHandler()
{
#if WINDOWS
return null;
#elif ANDROID
var handler = new CustomAndroidMessageHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
if (cert != null && cert.Issuer.Equals("CN=localhost"))
return true;
return errors == SslPolicyErrors.None;
};
return handler;
#else
throw new PlatformNotSupportedException("Only Windows and Android currently supported.");
#endif
}
#if ANDROID
internal sealed class CustomAndroidMessageHandler : Xamarin.Android.Net.AndroidMessageHandler
{
protected override Javax.Net.Ssl.IHostnameVerifier GetSSLHostnameVerifier(Javax.Net.Ssl.HttpsURLConnection connection)
=> new CustomHostnameVerifier();
private sealed class CustomHostnameVerifier : Java.Lang.Object, Javax.Net.Ssl.IHostnameVerifier
{
public bool Verify(string? hostname, Javax.Net.Ssl.ISSLSession? session)
{
return
Javax.Net.Ssl.HttpsURLConnection.DefaultHostnameVerifier.Verify(hostname, session)
|| hostname == "10.0.2.2" && session.PeerPrincipal?.Name == "CN=localhost";
}
}
}
#endif
}
在安卓机里面是没有 localhost 的,也没有 127.0.0.1 代表本机的取而代之的是 10.0.2.2 。在 HTTPS 里面,证书是被强制使用的,本地 Web API 会有一个默认证书,他里面带一个 “CN=localhost” 所以上面的类针对这个进行了过滤,并且 Android 的底层用的不是默认的 .net 的 HttpClient ,使用了 GetPlatformMessageHandler 进行了重写,这个可是Java 的包。
我的本地调用使用
var devSslHelper = new DevHttpsConnectionHelper(sslPort: 5001);
_blazorHubConnection = new HubConnectionBuilder()
#if ANDROID
.WithUrl(devSslHelper.DevServerRootUrl + "/hubs/devicehub"
, configureHttpConnection: o =>
{
o.HttpMessageHandlerFactory = m => devSslHelper.GetPlatformMessageHandler();
}
)
#else
.WithUrl(devSslHelper.DevServerRootUrl + "/hubs/devicehub")
#endif
.Build();
这个时候就不报 SSL 的连接错误了,可以方便的正常调试