Microsoft Office

探讨 JavaScript API for Office: 邮件应用程序

Angela Chu-Hatoun

下载代码示例

本文是深入探讨 JavaScript API for Office 的第四部分,重点讨论 Outlook 和 Outlook Web 应用程序所支持的邮件应用程序的可用 API 部分。 假设您对于 Office 的应用程序已有基本了解。 如果有疑问,阅读开发人员中心文档页上的“用于 Office 的应用程序概述”(bit.ly/12nBWHG) 可以获得相关知识。 本系列第一部分,“探索新的 JavaScript API for Office”(msdn.microsoft.com/magazine/jj891051) 高度概述了以 Office 对象为根对象的对象模型。 Office.context 中的 Mailbox 对象为对象模型中绝大多数邮件应用程序专用功能提供了入口,我将从这里开始讨论。

本文的主题是向开发者介绍 JavaScript API for Office 在邮件应用程序中的应用。 我还在以下地址提供了一篇在线姊妹篇文章,其中对一个邮件应用程序示例进行了剖析,并提供了源代码:msdn.microsoft.com/magazine/dn205107。 现在我要讨论 JavaScript API for Office 的各种功能,我们先从较基本的常用技巧开始,然后介绍比较高深的概念。

JavaScript API for Office 的基本功能

Mailbox 对象提供对用户配置文件、用户当前选择的项目、显示项目的窗体和操控电子邮箱中项目和文件夹的 Exchange Web Services (EWS) 子集的访问。 Item 对象表示选定的邮件或约会项。 通过该对象,可以进一步访问内置属性、自定义属性和该项目的附件。

根据项类型控制激活 可以在清单中定义规则,用以确定何时激活邮件应用程序。 用户当前选定并由 Mailbox.item 指定的项可以是一个“邮件”(包括会议请求、响应和取消通知)对象或“约会”对象。 根据具体方案,可以限制邮件应用程序,使其只激活特定类型的项。 以下 XML 示例显示的是一个 ItemIs 激活规则,用来限制邮件应用程序,使其只激活消息类项:

<Rule xsi:type="ItemIs" ItemType="Message" />

使用 JavaScript API for Office,可以利用 itemType 属性确认选定项的类型:

Office.context.mailbox.item.itemType

项访问和属性 邮件应用程序提供的一个基本功能是访问当前选定的项及其内置属性。

以下 JavaScript 示例显示如何访问选定的项及其内置属性 subject:

// The initialize function is required for all apps.
Office.initialize = function () {   // Check for the DOM to load.
$(document).ready(function () {     var item = Office.context.mailbox.item;     var subject = item.subject;     // Continue processing the subject.
}); }

已知实体 无论您是否使用实体激活邮件应用程序,Exchange Server 都能识别可用的部分实体。 如果实体存在于选定项的主题或正文内,Outlook 和 Outlook Web App 会提取它们,并通过以下 JavaScript API 方法使其可用:

  • getEntities 方法
  • getEntitiesByType(entityType) 方法
  • getFilteredEntitiesByName(name) 方法

所支持的实体包括地址、联系人、电子邮件地址等。

请注意,无论应用程序清单中指定何种默认区域设置,Outlook 和 Outlook Web App 都只能提取英文字符串。 它们不能提取“已发送邮件”文件夹中的实体。 它们能提取消息类型的会议建议,不能提取约会类型的建议。

获取可产生上下文激活的匹配项 可以指定依赖于正则表达式匹配(ItemHasRegularExpressionMatch 规则)或选定项中实体匹配(ItemHasKnownEntity 规则)的激活规则。 可以使用以下方法获取正则表达式匹配: 这些方法在父级 Item 对象中可用,而该对象可以是消息类型或约会类型的:

  • getRegExMatches 方法
  • getRegExMatchesByName(name) 方法

可以使用“已知实体”部分列出的方法获取特定实体匹配。 请注意,无论邮件应用程序使用何种类型的激活规则,始终可以使用这些方法获取任何实体匹配。

示例 以下是一个名为 VideoURL 的 ItemHasRegularExpressionMatch 规则示例,如果选定的项是一封邮件且邮件正文包含 YouTube 上某个视频的 URL,则它会激活邮件应用程序:

<Rule xsi:type="RuleCollection" Mode="And">   <Rule xsi:type="ItemIs" ItemType="Message"/>   <Rule xsi:type="ItemHasRegularExpressionMatch"     RegExName="VideoURL"     RegExValue="http://www\.youtube\.com/watch\?v=[a-zA-Z0-9_-]{11}"     PropertyName="Body"/> </Rule>

以下 JavaScript 代码示例显示如何使用 Item.getRegExMatches 方法获取前面 VideoURL 正则表达式的匹配项:

Office.initialize = function () {   // Check for the DOM to load.
$(document).ready(function () {     // Get an array of string matches for the regular expression VideoURL,     // as specified in the manifest.
var matches = Office.context.mailbox.item.getRegExMatches().VideoURL;     // Continue processing the matches for the regular expression.
}); }

以下是 ItemHasKnownEntity 规则的示例,如果邮件主题或正文中包含地址,该规则将激活邮件应用程序:

<Rule xsi:type="RuleCollection" Mode="And">   <Rule xsi:type="ItemIs" ItemType="Message"/>   <Rule xsi:type="ItemHasKnownEntity" EntityType="Address" /> </Rule>

以下 JavaScript 代码示例显示如何使用 getEntitiesByType 方法获取当前选定邮件主题或正文中邮寄地址的字符串数组:

Office.initialize = function () {   // Check for the DOM to load.
$(document).ready(function () {     var item = Office.context.mailbox.item;     // Get an array of strings that represent postal     // addresses in the current item.
var addresses = item.getEntitiesByType(       Office.MailboxEnums.EntityType.Address);     // Continue processing the array of addresses.
}); }

基于上下文激活邮件应用程序(特别是正则表达式匹配或存在已知实体时)的功能十分强大,调试起来可能比较复杂。 关于如何调试激活问题的技巧,请参见以下地址的 MSDN 库文章“邮件应用程序激活故障排除”:bit.ly/11C0H30

按项按应用程序数据存储 CustomProperties 对象可以用来存储项特定的数据,这些数据在随后的会话中作为项的自定义属性使用,只有您的邮件应用程序可以访问它们。 此类数据用键-值对表示。 如果应用程序设计为在多种 Outlook 客户端和设备上运行,则自定义属性可以随受支持的 Outlook 客户端和设备漫游。

项的自定义属性通过 Item.loadCustomPropertiesAysnc 方法提供。 这是一种以回调方法作为参数的异步方法。 当加载自定义属性时,它们通过 asyncResult.value 属性的形式提供,并作为输入参数传入回调方法。 图 1 显示如何加载、获取、设置和保存当前项的自定义属性。

图 1 使用当前项的自定义属性

Office.initialize = function () {   var mailbox = Office.context.mailbox;   mailbox.item.loadCustomPropertiesAsync(customPropsCallback); } function customPropsCallback(asyncResult) {   if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {     var customProps = asyncResult.value;     var myProp = customProps.get("myProp");     customProps.set("otherProp", "value");     customProps.saveAsync(saveCallback);   }   else {     write (asyncResult.error.message);   } } function saveCallback(asyncResult) {   // Callback method to save custom properties.
}

按邮箱按应用程序数据存储 RoamingSettings 对象可用来存储邮件应用程序可以访问的同一邮箱的自定义数据,无论邮件应用程序运行于桌面、平板电脑还是智能手机上。 以下 JavaScript 示例显示如何在邮件应用程序的 initialize 事件处理程序中获取邮箱特定数据:

var _mailbox; var _settings; Office.initialize = function () {   // Check for the DOM to load using the jQuery ready function.
$(document).ready(function () {     // Initialize instance variables to access API objects.
_mailbox = Office.context.mailbox;     _settings = Office.context.roamingSettings;     // Continue with app-specific code.
}); }

用户配置文件 使用 Office.context.mailbox.userProfile 属性可以访问用户的配置文件。 通过 UserProfile 对象可以获取登录用户的显示名称、SMTP 地址和本地时区。 以下 JavaScript 代码示例显示如何访问登录 Outlook 或 Outlook Web App 的当前用户的 UserProfile.emailAddress 属性:

Office.initialize = function () {   // Check for the DOM to load.
$(document).ready(function () {     var userProfile = Office.context.mailbox.userProfile;     var SMTPAddress = userProfile.emailAddress;     // Continue with processing the user's SMTP address.    }); }

显示 Outlook 窗体 在 Outlook 中,用户可以指定默认情况下显示约会和邮件的窗体。 通过使用 JavaScript API for Office 中的以下方法,邮件应用程序可以使用与 Outlook 使用的相同窗体分别显示约会和邮件:

  • Mailbox.displayAppointmentForm(itemId)
  • Mailbox.displayMessageForm(itemId)

图 2 中的代码检查用户是否选择了约会并使用 Outlook 默认约会窗体显示该约会。

图 2 检查用户是否选择了约会

var myOm; var myItem; Office.initialize = function () {   $(document).ready(function () {     myOm = Office.context.mailbox;     // Get the selected item.
myItem = myOm.item;     // Display the selected appointment.
displaySelectedAppointment();   }); } function displaySelectedAppointment() {   // Display selected item only if the item is an appointment.
if (myitem.itemType == Office.MailboxEnums.ItemType.Appointment)   {     myOm.displayAppointmentForm(myItem.itemId);   }   else   {     // Handle condition as appropriate.
} }

Mailbox.displayNewAppointmentForm 方法可用来为新会议在约会窗体中指定某些字段的值,并显示该窗体供用户填写约会信息。 调用此方法时,需将这些字段指定为字符串-值对列表,如图 3 所示。

图 3 指定新约会窗体中特定字段的值

var myOm; Office.initialize = function () {   $(document).ready(function () {     myOm = Office.context.mailbox;     // After the DOM is loaded, can display the     // pre-populated new appointment form.
displayFormForNewAppointment();   }); } function displayFormForNewAppointment(){   var formParameters =   {     "requiredAttendees" : ["wendy@contoso.com", "derek@contoso.com"],     "optionalAttendees" : ["shane@contoso.com", "michael@contoso.com"],     "start" : new Date("September 27, 2012 11:15:00"),     "end" : new Date("("September 27, 2012 12:30:00"),     "location" : "Conf room 200",     "resources" : ['sound', 'lighting', 'recording'],     "subject" : "Next rehearsal",     "body" : "This is about the next rehearsal."   }   // Display a form to create an appointment with   // the specified fields in the form.
myOm.displayNewAppointmentForm(formParameters); }

显示约会或邮件回复窗体的方法具有极大的自定义空间:

  • Appointment.displayReplyAllForm(htmlBody) 方法
  • Appointment.displayReplyForm(htmlBody) 方法
  • Message.displayReplyAllForm(htmlBody) 方法
  • Message.displayReplyForm(htmlBody) 方法

您可以使用这些方法中的任何一个提供表示回复窗体正文的 HTML 字符串。 图 4 中的示例显示了邮件的“全部回复”窗体。

图 4 指定邮件项“全部回复”窗体的 HTML 代码

var myOm; var myItem; Office.initialize = function () {   // Check for the DOM to load.
$(document).ready(function () {     myOm = Office.context.mailbox;     // Get the selected item.
myItem = myOm.item;     // After the DOM is loaded, display     // the reply form.
displayReply();   }); } function displayReply(){   // Display a reply form to the sender and recipients only   // if the selected item is a message.
if (myitem.itemType == Office.MailboxEnums.ItemType.Message) {       myItem.displayReplyAllForm       ("<html><head><head><body>This is the body of " +        "the email reply.</body></html>");   } }

高级概念

获取项附件 邮件应用程序可使用 JavaScript API for Office 和 EWS 回调令牌访问用户邮箱中当前选定邮件或约会的附件。 使用回调令牌可以允许邮件应用程序(或第三方 Web 服务)的后端服务器侧代码调用 EWS 操作 GetAttachment,以便实际获取特定的附件。

下面介绍邮件应用程序如何获取附件(图 5 中也对此过程进行了说明):

  1. 邮件应用程序使用 Mailbox.getCallbackTokenAsync 请求回调令牌,使用 Item.attachments 获取选定约会或邮件的附件(包括附件 ID),并使用 Mailbox.ewsUrl 获取当前电子邮件帐户 EWS 端点的 URL。
  2. 邮件应用程序将回调令牌、附件 ID 和 EWS 端点的 URL 传入服务器端代码。
  3. 服务器端代码调用 EWS GetAttachment 操作从该 Exchange 存储中获取实际附件。

Mail Apps Accessing Attachments from an Exchange Server
图 5 邮件应用程序从 Exchange 服务器访问附件

请注意,服务器端代码应通过直接创建 EWS 请求来访问 EWS。 无法使用 Mailbox.makeEws­RequestAsync 方法访问 GetAttachment 操作。 到撰写本文时为止,如果邮件应用程序在 Outlook Web App 上运行,则支持访问附件。 如果邮件应用程序运行于 Outlook,则该功能不可用。

关于如何在邮件应用程序中访问附件的示例,请参见 MSDN 代码示例“Office 邮件应用程序: 从 Exchange 服务器获取附件”(bit.ly/11dG9fS)。

单一登录的用户令牌 您的邮件应用程序可使用常见的身份验证 Web 手段对用户进行验证和授权。 如果要跨 Outlook 和 Outlook Web App 支持用户单一登录身份验证,可以通过调用 Mailbox.getUserIdentityTokenAsync 方法来使用 Exchange 标识令牌。

单一登录基于 Exchange Server,为该 Exchange Server 上的用户电子邮件帐户分配一个 Exchange 标识令牌。 Exchange 标识令牌包含多个字段,其中有用于验证令牌的签名和表示 Exchange 电子邮件帐户的唯一标识符。

一般而言,负责用户身份验证的邮件应用程序可以与 Web 服务(可以是邮件应用程序的服务器侧代码)或第三方 Web 服务(在联合身份验证系统中使用其自身的标识提供程序 (IdP) 验证用户身份)交互。 单一登录的关键是 Web 服务在唯一 ID(从 Exchange 标识令牌获取)和用户标识(对 IdP 已知)之间维护的映射关系。

图 6 对使用 Exchange 标识令牌进行身份验证的过程作了简要说明。

该过程总结如下:

  1. 邮件应用程序调用 Mailbox.getUserIdentityTokenAsync 请求提供用户的 Exchange 标识令牌,指定当异步获取操作完成时运行的回调方法。
  2. 回调函数获取 Exchange 标识令牌并将其传入 Web 服务进行身份验证。
  3. Web 服务从 Exchange Server 获取公钥以验证令牌。
  4. 根据用户以前是否登录,Web 服务将进行以下处理:
    1. 如果用户第一次登录邮件应用程序,则该帐户不存在事先映射,且 Web 服务响应邮件应用程序提示用户输入凭据。 收到凭据后,邮件应用程序将其传入 Web 服务。
    2. 然后,服务在令牌提供的唯一 ID 和从 IdP 已知的凭据获取的用户标识之间创建映射。 服务使用凭据可以使用户登录。
    3. 接下来: 当用户从桌面、平板电脑或智能手机浏览器打开 Outlook 或 Outlook Web App 中的邮件应用程序时,会发生以下步骤:
      1. 邮件应用程序调用 getUserIdentityTokenAsync 并从令牌获取相同的唯一 ID
      2. 邮件应用程序将令牌传入 Web 服务。
      3. Web 服务进行令牌验证。
      4. 然后 Web 服务查询映射表、查找用户并授权访问。

图 6 使用 Exchange 标识令牌进行身份验证

验证令牌后,可以使用户保持登录,即使用户从其他主机应用程序或设备打开电子邮件应用程序也可以保持。

身份验证和验证的细节非常复杂。 可以使用“EWS 托管的 API 验证库”简化该过程。 使用验证库,Web 服务可以创建对象,提取令牌并将其放入对象中,检验签名是否有效,如果有效,则将唯一标识符放入映射数据库中。

有关使用 Exchange 标识令牌进行身份验证的详细信息,请参阅以下开发人员中心文档:

对于代码示例:

  • 有关使用 Exchange 标识令牌进行身份验证的邮件应用程序和 Web 服务的 C# 示例,可以参考 Visual Studio 2012 C# 中的“Outlook 邮件应用程序: 使用客户端标识令牌”示例 (bit.ly/ZxS85b)。
  • 有关如何使用 .NET Framework 验证 Exchange 客户端标识令牌的示例,请参见“Outlook 邮件应用程序: 使用 .NET Framework 验证客户标识令牌”(bit.ly/17j9FSZ)。

访问 EWS 的子集 使用 Mailbox.makeEwsRequestAsync 方法,邮件应用程序可以访问 EWS 操作的子集来创建、查找、获取、更新、移动或发送项或文件夹。 例如,使用 CreateItem 操作可以创建约会、邮件或联系人。 可以使用 FindItem 操作在邮箱中查找项。 可以使用 SendItem 操作发送邮件或会议邀请。 所支持的操作子集仅与用户的邮箱有关。 其他与整个组织有关的 EWS 操作(例如访问全局地址列表或循环访问组织中每一个人)不属于邮件应用程序的范畴。 此外,由于所支持的操作子集功能十分强大,邮件应用程序必须在应用程序清单中请求读/写邮箱权限,且只有管理员能安装需要此权限的邮件应用程序。

调用 Mailbox.makeEwsRequestAsync 方法其实就是在托管用户邮箱的 Exchange Server 上请求 EWS。 调用 makeEwsRequestAsync 前,应指定下列参数的输入参数:

  • 数据参数: 创建希望调用的 EWS 操作的 XML SOAP 请求。
  • 回调参数: 用来处理操作的响应的回调方法。
  • userContext 参数: 回调方法的任何输入数据。

操作完成时,Office 框架的应用程序通过一个参数(即 AsyncResult 对象)调用回调方法,该方法与您在本系列第一部分见到的其他异步方法相似。 回调方法可以访问 Async­Result.value 属性获取包含操作相关数据的 XML SOAP 响应。 回调方法还可访问 AsyncResult.asyncContext 属性,以此访问通过 userContext 参数传入的任何其他输入参数。 然后,回调方法通过解析 SOAP 响应中的 XML 来获取其响应的相关数据。

有关所支持的 EWS 操作列表和其他详细信息,请参见开发人员中心文档“从 Outlook 邮件应用程序调用 Web 服务”(bit.ly/12jtH0z)。

有关介绍如何调用 GetItem 操作的邮件应用程序的示例,请参见 Visual Studio 2012 C# 中的“Outlook 邮件应用程序: 提出 EWS 请求”示例 (bit.ly/Zv3hEQ)。

总结

至此,我对于构建邮件应用程序的讨论和 JavaScript API for Office 的四部分探讨就结束了。 有关此系列以前的部分和本部分的在线姊妹篇文章,请参见:

Angela Chu-Hatoun 最初是一名软件开发者,后来出于对介绍软件工作原理的强烈兴趣而转为写作。 她担任 Office 部门的程序员已有 12 年,负责为开发者编写关于如何创建 Office 应用程序和其他 Outlook 解决方案的文章。 她喜爱阅读时事、园艺、旅游、美食和时装。

衷心感谢以下技术专家对本文的审阅: Andrew Salamatov (Microsoft) 和 Tim Wan (Microsoft)
Tim Wan 2003 年毕业于加州理工学院,持有“工程和应用科学”学位。 在过去九年中,他一直是一名 Outlook 软件开发者。 他大量参与了 Outlook 2013 的邮件应用程序功能开发和以前版本的 Outlook 对象模型开发。 他期望为世界各地的客户带来新的功能和改进。