从 Android 仿真器和 iOS 模拟器连接到本地 Web 服务

Browse sample. 浏览示例

许多移动和桌面应用使用 Web 服务。 在软件开发阶段,通常会在本地部署 Web 服务,并通过 Android 模拟器或 iOS 模拟器中运行的应用使用它。 这可以避免将 Web 服务部署到托管终结点,并实现简单的调试体验,因为应用和 Web 服务都在本地运行。

在 Windows 或 MacCatalyst 上运行的 .NET 多平台应用 UI (.NET MAUI) 应用可以使用通过 HTTP 或 HTTPS 在本地运行的 ASP.NET Core Web 服务,而无需执行任何其他工作,前提是你信任你的开发证书。 但是,当应用在 Android 模拟器或 iOS 模拟器中运行时,还需要执行其他工作,该过程因 Web 服务是通过 HTTP 还是 HTTPS 运行而异。

本地计算机地址

Android 模拟器和 iOS 模拟器都提供对在本地计算机上通过 HTTP 或 HTTPS 运行的 Web 服务的访问权限。 但它们的本地计算机地址各不相同。

Android

Androidnullnull模拟器的各个实例独立于开发计算机网络接口,在虚拟路由器的后方运行。 因此,模拟设备无法看到开发计算机或网络中的其他模拟器实例。

但是,各个模拟器的虚拟路由器托管着一个包含预分配地址的特殊网络空间,其中 10.0.2.2 地址是主机环回接口的别名(在开发计算机上为 127.0.0.1)。 因此,对于通过 /api/todoitems/ 相对 URI 公开 GET 操作的本地 Web 服务,在 Android 模拟器中运行的应用可以通过向 http://10.0.2.2:<port>/api/todoitems/https://10.0.2.2:<port>/api/todoitems/ 发送 GET 请求来使用操作。

iOS

iOS 模拟器使用主机网络。 因此,模拟器中运行的应用可以通过计算机 IP 地址或通过 localhost 主机名连接到本地计算机上运行的 Web 服务。 例如,对于通过 /api/todoitems/ 相对 URI 公开 GET 操作的本地 Web 服务,在 iOS 模拟器中运行的应用可以通过向 http://localhost:<port>/api/todoitems/https://localhost:<port>/api/todoitems/ 发送 GET 请求来使用操作。

注意

从 Windows 在 iOS 模拟器中运行 .NET MAUI 应用时,该应用会显示在适用于 Windows 的远程 iOS 模拟器中。 但是,应用在配对的 Mac 上运行。 因此,对于在 Mac 上运行的 iOS 应用,在 Windows 中运行的 Web 服务没有 localhost 访问权限。

通过 HTTP 运行的本地 Web 服务

在 Android 模拟器或 iOS 模拟器中运行的 .NET MAUI 应用可以使用通过 HTTP 在本地运行的 ASP.NET Core Web 服务。 这可以通过配置 .NET MAUI 应用项目和 ASP.NET Core Web 服务项目从而允许明文 HTTP 流量来实现。

在 .NET MAUI 应用中定义本地 Web 服务的 URL 的代码中,确保 Web 服务 URL 指定 HTTP 方案和正确的主机名。 DeviceInfo 类可用于检测正在运行应用的平台。 然后,可以按如下所示设置正确的主机名:

public static string BaseAddress =
    DeviceInfo.Platform == DevicePlatform.Android ? "http://10.0.2.2:5000" : "http://localhost:5000";
public static string TodoItemsUrl = $"{BaseAddress}/api/todoitems/";

有关 DeviceInfo 类的详细信息,请参阅设备信息

此外,要在 Android 上运行应用,必须添加所需的网络安全配置;要在 iOS 上运行应用,必须选择退出 Apple 传输安全性 (ATS)。 有关详细信息,请参阅 Android 网络安全配置iOS ATS 配置

还必须确保 ASP.NET Core Web 服务配置为允许 HTTP 流量。 这可以通过将 HTTP 配置文件添加到 ASP.NET Core Web 服务项目中 launchSettings.json 的 profiles 部分来实现:

{
  ...
  "profiles": {
    "http": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "launchUrl": "api/todoitems",
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    ...
  }
}

然后,在 Android 模拟器或 iOS 模拟器中运行的 .NET MAUI 应用可以使用通过 HTTP 在本地运行的 ASP.NET Core Web 服务,前提是 Web 服务通过 http 配置文件启动。

Android 网络安全配置

若要在 Android 上启用明文本地流量,必须创建网络安全配置文件。 这可以通过将名为 network_security_config.xml 的新 XML 文件添加到 .NET MAUI 应用项目中的 Platforms\Android\Resources\xml 文件夹来实现。 XML 文件应指定以下配置:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="true">10.0.2.2</domain>
  </domain-config>
</network-security-config>

注意

确保 network_security_config.xml 文件的生成操作设置为 AndroidResource。

然后,配置 .NET MAUI 应用项目中 Platforms\Android\AndroidManifest.xml 文件内 application 节点的 networkSecurityConfig 属性:

<?xml version="1.0" encoding="utf-8"?>
<manifest>
    <application android:networkSecurityConfig="@xml/network_security_config" ...>
        ...
    </application>
</manifest>

有关网络安全配置文件的详细信息,请在 developer.android.com 参阅网络安全配置

iOS ATS 配置

若要在 iOS 上启用明文本地流量,应在 .NET MAUI 应用中选择退出 Apple 传输安全性 (ATS)。 这可以通过将以下配置添加到 .NET MAUI 应用项目中的 Platforms\iOS\Info.plist 文件来实现:

<key>NSAppTransportSecurity</key>    
<dict>
    <key>NSAllowsLocalNetworking</key>
    <true/>
</dict>

有关 ATS 的详细信息,请在 developer.apple.com 参阅防止不安全的网络连接

通过 HTTPS 运行的本地 Web 服务

在 Android 模拟器或 iOS 模拟器中运行的 .NET MAUI 应用可以使用通过 HTTPS 在本地运行的 ASP.NET Core Web 服务。 启用它的过程如下所示:

  1. 信任计算机上的自签名开发证书。 有关详细信息,请参阅信任你的开发证书
  2. 指定本地计算机的地址。 有关详细信息,请参阅指定本地计算机地址
  3. 绕过本地开发证书安全检查。 有关详细信息,请参阅绕过证书安全检查

将依次讨论每项。

信任你的开发证书

安装 .NET Core SDK 会将 ASP.NET Core HTTPS 开发证书安装到本地用户证书存储。 但是,尽管证书已安装,但它不受信任。 若要信任该证书,请执行以下一次性步骤,以运行 dotnet dev-certs 工具:

dotnet dev-certs https --trust

下面的命令提供有关 dev-certs 工具的帮助:

dotnet dev-certs https --help

或者,当你运行使用 HTTPS 的 ASP.NET Core 2.1 项目(或更高版本)时,Visual Studio 将检测是否缺少开发证书并提供安装和信任。

注意

ASP.NET Core HTTPS 开发证书是自签名证书。

有关在计算机上启用本地 HTTPS 的详细信息,请参阅启用本地 HTTPS

指定本地计算机地址

在 .NET MAUI 应用中定义本地 Web 服务的 URL 的代码中,确保 Web 服务 URL 指定 HTTPS 方案和正确的主机名。 DeviceInfo 类可用于检测正在运行应用的平台。 然后,可以按如下所示设置正确的主机名:

public static string BaseAddress =
    DeviceInfo.Platform == DevicePlatform.Android ? "https://10.0.2.2:5001" : "https://localhost:5001";
public static string TodoItemsUrl = $"{BaseAddress}/api/todoitems/";

有关 DeviceInfo 类的详细信息,请参阅设备信息

绕过证书安全检查

尝试从 Android 模拟器中运行的 .NET MAUI 应用调用本地安全 Web 服务将导致引发 java.security.cert.CertPathValidatorException,并显示一条消息,指示找不到证书路径的信任定位点。 同样,尝试从 iOS 模拟器中运行的 .NET MAUI 应用调用本地安全 Web 服务将导致 NSURLErrorDomain 错误,并显示一条消息,指示服务器的证书无效。 发生这些错误的原因是本地 HTTPS 开发证书是自签名证书,而 Android 或 iOS 不信任自签名证书。 因此,当应用使用本地安全 Web 服务时,有必要忽略 SSL 错误。

这可以通过将配置的本机 HttpMessageHandler 类版本传递给 HttpClient 构造函数来实现,这会指示 HttpClient 类通过 HTTPS 信任 localhost 通信。 HttpMessageHandler 类是一个抽象类,它在 Android 上的实现由 AndroidMessageHandler 类提供,在 iOS 上的实现由 NSUrlSessionHandler 类提供。

以下示例演示了一个类,该类配置了 Android 上的 AndroidMessageHandler 类和 iOS 上的 NSUrlSessionHandler 类以信任通过 HTTPS 的 localhost 通信:

public class HttpsClientHandlerService
{
    public HttpMessageHandler GetPlatformMessageHandler()
    {
#if ANDROID
        var handler = new Xamarin.Android.Net.AndroidMessageHandler();
        handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
        {
            if (cert != null && cert.Issuer.Equals("CN=localhost"))
                return true;
            return errors == System.Net.Security.SslPolicyErrors.None;
        };
        return handler;
#elif IOS
        var handler = new NSUrlSessionHandler
        {
            TrustOverrideForUrl = IsHttpsLocalhost
        };
        return handler;
#else
     throw new PlatformNotSupportedException("Only Android and iOS supported.");
#endif
    }

#if IOS
    public bool IsHttpsLocalhost(NSUrlSessionHandler sender, string url, Security.SecTrust trust)
    {
        if (url.StartsWith("https://localhost"))
            return true;
        return false;
    }
#endif
}

在 Android 上,GetPlatformMessageHandler 方法会返回一个 AndroidMessageHandler 对象。 GetPlatformMessageHandler 方法将 AndroidMessageHandler 对象上的 ServerCertificateCustomValidationCallback 属性设置为一个回调,该回调忽略本地 HTTPS 开发证书的证书安全检查结果。

在 iOS 上,GetPlatformMessageHandler 方法会返回一个 NSUrlSessionHandler 对象,该对象将其 TrustOverrideForUrl 属性设置为名为 IsHttpsLocalHost 的委托,它与 NSUrlSessionHandler.NSUrlSessionHandlerTrustOverrideForUrlCallback 委托的签名匹配。 当 URL 以 https://localhost 开头时,IsHttpsLocalHost 委托会返回 true

然后,可以将生成的 HttpClientHandler 对象作为参数传递给调试生成的 HttpClient 构造函数:

#if DEBUG
            HttpsClientHandlerService handler = new HttpsClientHandlerService();
            HttpClient client = new HttpClient(handler.GetPlatformMessageHandler());
#else
            client = new HttpClient();
#endif

然后,在 Android 模拟器或 iOS 模拟器中运行的 .NET MAUI 应用可以使用通过 HTTPS 在本地运行的 ASP.NET Core Web 服务。