2016 年 5 月

第 31 卷,第 5 期

通用 Windows 平台应用 - 企业托管 Web 应用程序

作者 Tim Kulp | 2016 年 5 月

在结构化的企业中工作的开发人员都知道,实施新技术并不容易。如果你的团队对交付这一类型的应用并不精通(或没有信心),那么向企业介绍类似通用 Windows 平台 (UWP) 应用项目这样的新内容可能会成为一种挑战。许多企业对 Intranet Web 应用程序投入了大量的资金和团队支持。托管 Web 应用程序 (HWA) 为企业 Intranet Web 应用加入企业 Windows 应用商店提供了桥梁,且无需花费太多精力。

在本文中,我将把现有的 Intranet Web 应用程序转换为可用于 Windows 应用商店的 UWP 应用,然后使用本机 Windows 功能对应用进行增强。如图 1 所示,Web 应用程序是一个称为“Gotcha”的社会认可型应用,它让 Contoso Corp. 的员工可以识别同行的善举或表示感谢。Gotcha 的目的是互相庆祝,并构建更强大、更快乐的团队。此 Web 应用程序可轻松地集成联系人、共享和照相机,是 Windows 应用商店的理想之选。

Gotcha Web 应用程序
图 1 Gotcha Web 应用程序

企业的桥和托管 Web 应用程序

对于企业开发人员而言,UWP 桥和 HWA 的意义重大,因为开发人员可以利用其维护应用的现有工具、进程和部署系统。概念推动的桥使开发人员可以将现有的 iOS、Android、Win32 或 Web 应用程序引入 UWP 和跨平台 Windows 应用商店中。其目标是使开发人员可以将现有代码库引入 UWP 中,并使用户在特定于 Windows 的功能方面(例如 Xbox 成就)获得更好的体验。

桥可以轻松地将代码引入 UWP,但企业开发人员还面临着其他挑战,这使得代码转换出现困难。基于应用程序中信息的敏感性,企业的开发人员可能会面临诸多限制,例如,有哪些可用的代码编辑器,或者代码的部署位置。对于企业而言,UWP 桥的真正价值并不在于转换应用的便捷性,而在于它能够维护现有的企业软件开发工具、实践和变更管理,并通过 Windows 应用商店交付 UWP 应用。

例如,HWA(又名“Project Westminster”)使开发人员能够将 Web 应用程序嵌入 UWP 应用中。构建使用 HWA 内容的 UWP 应用后,开发人员即可使用现有的实践来维护 Web 应用程序,这将自动更新 UWP 应用的 Web 应用程序体验。使用 HWA 桥后,开发人员可以专注于 Web 应用程序,并通过功能检测将特定于 UWP 的功能包括在内。使用现有的 Intranet 是一个不错的 UWP 桥用例,但企业外部的开发人员还可以利用 HWA 将 Web 应用引入 Windows 应用商店。只要通过了 Windows 应用商店的认证过程,任何网站都可以成为 HWA。

Kiril Seksenov 的博客文章“Project Westminster 概述”(bit.ly/1PTxxW4) 中详细介绍了 HWA 的基本信息。我推荐大家阅读这篇文章,以便更深入地了解 HWA 的理念。

启动 Web HWA 项目

开始将 Gotcha Web 应用程序转换为 HWA 之前,我在 Visual Studio 中创建了一个新的 JavaScript UWP 应用。创建完应用后,我打开包清单,并将应用的起始页设置为 Web 应用程序的 URL,如图 2 所示。在本例中,Gotcha Web 应用程序在 localhost:6107 中运行。设置好起始页后,应用将显示指定地址的网页,而非应用的本地内容。

设置包清单的起始页
图 2 设置包清单的起始页

最后,设置包清单中的内容 URI。这些 URI 将告知你的应用,哪些网页可以访问 Windows 运行时 (WinRT) 库,URI 可以拥有怎样的访问级别。我曾听说过这一内容,描述为定义应用的停止位置和 Internet 的启动位置,我认为这是对如何使用内容 URI 的一幅不错的思维图像。在现有的 Gotcha Web 应用程序中,应用可进行的操作和应用可使用 Internet 进行的操作之间存在明显的区别。例如,在 Gotcha 中,用户可以将认可分享到社交网络(例如 LinkedIn)中。LinkedIn 网站并非 Gotcha UWP 应用的一部分;而是 Internet 的一部分。我将只包括直接在应用程序内部使用和需要 UWP API 访问权限的 URI。

通过防止访问未注册 URI 的 WinRT 库,使用内容 URI 让开发人员得以保护应用的用户。对于每个 URI,指定每个 WinRT 库所需的访问级别:

  • 全部: 在 Web 应用程序(在本例中为 Gotcha)上运行的 JavaScript 代码可以访问通过应用功能定义的任意和所有 UWP API。
  • 仅允许 Web: 在 Web 应用程序上运行的 JavaScript 代码可以执行应用程序包中包含的自定义 WinRT 组件,但无法访问所有的 UWP API。
  • 无: 在 Web 应用程序上运行的 JavaScript 代码无法访问本地 UWP API(此为默认设置)。

设置内容 URI 时,请记住,这是 UWP 应用用户的应用程序安全的重要组成部分。确保只向 UWP 应用的必需功能提供 URI 所需的最低权限,如图 3 所示。如非应用真正需要,请避免将根 URI WinRT 权限设置为“全部”。

将内容 URI 设置为运行应用所需的最低权限
图 3 将内容 URI 设置为运行应用所需的最低权限

我不建议删除项目创建过程中内置的现有内容(例如 .css、.js 和 WinJS 文件夹)。现有内容为开发人员提供了绝佳的框架,可用于将本地内容添加到 Web HWA 中。在本文的稍后部分,我将使用本地内容的这些文件夹创建部分功能,以补充 Web HWA 的脱机体验。

配置好包清单后,运行 UWP 应用。如图 4 所示,Web 应用程序将和其他 UWP 应用一样显示在应用窗口中。

Gotcha 应用为 Windows 应用商店做好准备
图 4 Gotcha 应用为 Windows 应用商店做好准备

调试托管 Web 应用程序

HWA 和纯粹的本机应用在调试体验方面稍有不同。打开 UWP 应用后,按下 F12。将显示 UWP 应用的 F12 开发人员工具。通过 F12 开发人员工具,我可以和在浏览器中一样调试、分析和测试 Web 应用程序。虽然 F12 开发人员工具中的所有功能对于 HWA 开发人员而言都很有用,但我觉得最有用的组件是在 Visual Studio 以外调试和分析网络活动。我使用这些功能来深入研究特定应用的行为,并了解在应用中提供意外体验的任何问题(例如,缓存)。

和在浏览器中使用 F12 一样,开发人员还可以更改 DOM 并根据不同的屏幕或窗口大小尝试不同的 UI 体验。这可以用来探讨基于应用的响应性需求对 Web 应用程序进行的布局更改。例如,对齐 UWP 应用以显示为边栏应该将应用重排,以提供更好的体验。如果调整大小未将应用重排,则可使用 F12 开发人员工具确定原因,并使用 DOM 进行实验,查看实现所需的重排需要的内容。

为你的 UWP 应用添加功能

基本 HWA 就绪后,我将深入了解 Web 应用程序的功能以启用 UWP API,并使用户获得更好的体验。Intranet Web 应用程序可能会具有某些 HWA 没有的限制。HWA 可以使用 UWP API 访问 UWP 设备的许多本地功能(例如照相机、位置和其他传感器)。开始之前,请思考实现将 Web 应用程序推向托管 Web 应用程序的用例。在 Gotcha 应用中,我希望用户可以从给与奖励的联系人中选择,而不只是键入人名并向认可附加图片。

开始之前,我在 Web 应用程序上创建了一个用于检测本机 API 命名空间的远程 GotchaNative.js 代码文件(GotchaNative.js 脚本文件将位于远程 Web 应用程序服务器上)并执行适当的代码,以触发本机用例。将以下代码添加到 NativeGotcha.js 代码文件中:

var GotchaNative = (function () {
  if (typeof Windows != 'undefined') {
    // Add find contact here
    // Add capture photo here
  }});

该代码会构建 GotchaNative 对象,它将保留 UWP API 的所有功能。将这些 API 集中化使得单个文件可以包括在具有添加的 UWP API 功能的页面上。我将隔离该文件,以便将包括该特定文件的内容 URI 明确声明为对所需的 UWP API 具有访问权限的 URI。这样,我就能实现最低权限的安全概念,并只为需要访问 UWP API 的 URI 提供权限。

隔绝该文件的另一个好处在于,还可以为其他本机平台做准备。我将在本文的稍后部分探讨这一内容,但是现在,请将这一文件看作将包括在 Gotcha Web 应用程序中的所有本机功能的大本营。

扩展现有功能

现在,我们已经创建了 GotchaNative 对象,我可以添加特定功能,将 HWA 与 UWP API 连接。在第一个用例中,Gotcha Web 应用程序让用户输入一个人名进行识别。在 UWP 中,用户拥有存储联系人的 People 应用,让用户选择比让他们键入人名要简单得多。将 GotchaNative.js 代码文件中的“在此处查找联系人”注释替换为以下内容:

this.FindContact = function () {
  var picker = new Windows.ApplicationModel.Contacts.ContactPicker();
  picker.desiredFieldsWithContactFieldType.append(
    Windows.ApplicationModel.Contacts.ContactFieldType.email);
  return picker.pickContactAsync();
}

该代码是与 ContactPicker API 的基本连接,可用于从联系人列表中选择联系人。你会在 rjs.azureWebsites.net 中发现可整合入 Web HWA 的 UWP API 清单。该网站列出了更多热门的 API 及其复制/粘贴就绪代码,以帮助构建 Web HWA 项目。

你可以在 Gotcha 网站的个人视图模型中找到添加人名的功能。将图 5 中的代码添加到个人视图模型。

图 5 添加到个人视图模型的代码

if (Windows != undefined) {
  var nativeGotcha = new NativeGotcha();
  nativeGotcha.FindContact().done(function (contact) {
    if (contact !== null) {
      $("#txtWho").val(contact.displayName);
    } else {
      // Write out no contact selected
    }
  });
}
else {
  $('#add-person').on('shown.bs.modal', function () {
    $("#txtWho").focus();
}

如果由于脚本作为 HWA 运行,对 Windows 对象进行定义,则图 5 中的代码使用 FindContact 方法。如果未定义 Windows 对象,则 Web 应用程序将继续使用现有的 Bootstrap 模式窗口来收集有关要识别的个人的信息。该代码使 Web 应用程序可以使用一种方法输入用于识别的个人,而 UWP 应用可以利用更具针对性的不同用户体验。

添加新功能

有时,启用 UWP API 会允许通常不存在于 Web 应用程序中的新功能。在 Gotcha HWA 中,我希望用户可以拍照并将其发送给其他 Gotcha 用户用于识别。这对于应用程序而言是个新功能,将不会存在于 Web 应用程序中。构建 HWA 时,请考虑 UWP API 为应用带来的机会。

在开始实现这一新功能之前,我先用 GotchaNative.js 代码文件中的以下代码替换“添加捕获照片”注释:

this.CapturePicture = function () {
  var captureUI = new Windows.Media.Capture.CameraCaptureUI();
  captureUI.photoSettings.format =
    Windows.Media.Capture.CameraCaptureUIPhotoFormat.png;
  return captureUI.captureFileAsync(
    Windows.Media.Capture.CameraCaptureUIMode.photo);
}

在该代码中,我将打开捕捉 UI,使用户能够拍摄照片并返回给呼叫方。如果定义了 Windows 对象,则需要在 Web 应用程序中启用这一新功能。我将使用功能检测(就像我在个人视图模型中使用的那样)将图 6 中的代码添加到主页视图模型中,这里是用于添加识别的代码的大本营。

图 6 要添加到主页视图模型的功能检测代码

$('#give-modal').on('shown.bs.modal', function () {
  if (typeof Windows != 'undefined') {
    var gotchaNative = new NativeGotcha();
    $("#btnCamera").click(function () {
      gotchaNative.CapturePicture().then(function (capturedItem) {
        // Do something with capturedItem;
      });
    }).show();
  }
  else {
    $("#btnCamera").hide();
  }
})

如果未定义 Windows 对象,我将在图 6 的代码中隐藏 btnCamera。如果未定义 Windows 对象,则我将配置 click 事件来触发 CapturePicture 功能。为简单起见,我对如何操作图片进行了注释。启用照相机后,我需要记住,HWA 仍然是一个 UWP 应用,需要启用网络摄像机功能,这可以通过包清单来完成。

企业 Web 应用程序可以转换为 HWA,并利用 Windows 应用商店的 UWP API。使用现有的 Web 应用程序使在 Windows 应用商店中构建 UWP 应用变得容易,但 HWA 会假定具有 Internet 连接。接下来,我将为你展示如何在不需要 Internet 访问的前提下确保应用工作。

脱机连接

UWP 应用可以让本地文件提供脱机或本地体验。使用本地内容让你可以断开部分 UWP API 调用与托管 Web 应用程序的连接。与已经完成的功能检测一样,与本地内容的链接可以通过激活链接来完成。对于 Gotcha 而言,我将添加使用本地映射控件的映射功能。

在 HWA 中,我可以使用曾在主页视图模式中使用过的相同的功能检测。使用正确的 URI 方案让 HWA 能够在 Web 内容和本地内容之间跳转。我将在主页屏幕添加以下链接:

<a href="ms-appx:///default.html" id="lnkMap">
  <span class="glyphicon glyphicon-map-marker">&nbsp;</span> Show Map
</a>

该链接将与 ms-appx:/// 方案连接,它使应用程序与应用程序包中的本地内容连接。在 ms-appx 方案中工作时,应用可以按需与 UWP API 进行连接。这与使用内容 URI 来声明页面具有的访问 API 的访问级别类似。使用 ms-appx 与将 URI 标记为“全部”相似。在本例中,默认 .html 页面有权访问 UWP API。当 HWA 中出现链接时,用户可以单击该链接,使其在应用程序包中工作。

对于企业开发人员而言,该功能使其能够隔离特定于 UWP 的用例,且无需连接至 Web 应用程序。连接至包内容还能使你避免为 Web 应用程序提供 UWP API 的访问权限,并将所有访问权限保留在包中。

不止一个商店,怎么办?

根据企业不同,可能存在自带设备环境。这意味着,企业可能已经在使用现有应用针对 IOS 或 Android 设备了。Android 和 iOS 应用的概念与 HWA 类似,称之为 WebView 控制。WebView 让开发人员可以在应用中为现有的 Web 应用程序提供窗口,然后与 Web 应用程序进行交互,就像它是本机应用的一部分一样(听起来耳熟吗?)。开发人员可以构建 HWA 来配合其他应用,并针对交付 Web 应用的平台调整现有 Web 应用程序的功能。接下来,我将对 GotchaNative.js 进行更新以支持 Android,并展示 HWA 代码如何在 JavaScript 针对其他平台的情况下共存。

之前,我曾设置过 GotchaNative.js,目的是将所有的本机代码保留在应用中。ViewModels 将处理这些本机方法的输出。使用任意平台中的 API 与 HWA 类似: 首先,应用程序必须确定本机 API 是否可用,然后必须调用合适的 API。在更新 GotchaNative.js 之前,我将添加能告知我识别了哪些本机平台(如有)的属性。对于以下示例,我将假定 Android 应用正在使用将“Android”声明为本地脚本命名空间的 WebView:

this.Platform = function () {
  if (Windows != 'undefined')
    return "Windows";
  else if (Android != 'undefined')
    return "Android";
  else
    return "Web";
}

该功能让我的代码可以确定将要使用的平台,以便我可以针对平台调整代码和响应。从现在开始,GotchaNative.js 中的每个方法都可以检查平台以了解如何继续,如图 7 所示。

图 7 平台的 GotchaNative.js 方法检查

this.CapturePicture = function () {
  switch (this.Platform()) {
    case "Windows":
      var captureUI = new Windows.Media.Capture.CameraCaptureUI();
      captureUI.photoSettings.format =
        Windows.Media.Capture.CameraCaptureUIPhotoFormat.png;
      return captureUI.captureFileAsync(
        Windows.Media.Capture.CameraCaptureUIMode.photo);
      break;
    case "Android":
      Android.CaptureFile(); // Calls Android code in Native app
      break;
    default:
      return null;
      break;
}

现在,我将对 ViewModels 中的代码进行更新,以使用平台检测来了解要对 GotchaNative 方法的输出进行哪些操作。以下是在 HomeViewModel 中对照相机按钮事件进行的更新:

$("#btnCamera").click(function () {
  switch (gotchaNative.Platform()) {
    case "Windows":
      gotchaNative.CapturePicture().then(function (capturedItem) {
        // Do something with capturedItem;
      });
      break;
    case "Android":
      // Handle Android output
      break;
  }
}).show();

现在,由于应用能够支持更多的平台,我可以在代码中的一个位置为所有平台开发新功能。如果明天发布了新的 OS,开发团队将能够使代码适应新平台。

总结

企业开发人员在工作中面临着多种挑战。标准化的过程和工具可以被视为实现新技术(例如 UWP 应用)的障碍。使用 UWP 桥,企业开发人员可以将现有的 Intranet 或外部 Web 应用引入 Windows 应用商店(无论是公共还是企业 Windows 应用商店)。HWA 桥可以将 Web 应用程序变为跨平台的 UWP 应用,它可利用 UWP API 提供更好的用户体验。

在本文中,我探讨了如何将现有的 Web 应用程序转换为 HWA 并添加特定于 UWP 应用的功能(例如访问照相机和联机人)的方法。我使用 ms-appx 方案展示了如何从你的托管 Web 应用程序跳转至应用程序包中的本地内容。最后,我使用一些规划展示了如何使用多种不同的平台构建 HWA,使企业能够紧跟这个不断扩展的移动设备世界的方法。使用这些技术,HWA 可以利用 Web 应用程序中的现有投资,帮助企业加入 Windows 应用商店的生态系统。


Tim Kulp是一名高级技术架构师,居住在美国马里兰州巴尔的摩。他身兼数职:Web 开发者、移动开发者、UWP 开发者、作者、画家、父亲以及“要成为疯狂科学家”奠基人。 请通过 Twitter @seccode 或 LinkedIn linkedin.com/in/timkulp 与他联系。

衷心感谢以下 Microsoft 技术专家对本文的审阅: John-David Dalton 和 Kiril Seksenov