精通-ServiceNow-脚本编程-全-
精通 ServiceNow 脚本编程(全)
原文:
zh.annas-archive.org/md5/535d1bd00848e9abf39377b172ea1a58译者:飞龙
前言
ServiceNow 正逐渐成为 IT 服务管理的首选平台。像 RedHat 和 NetApp 这样的行业巨头已经采用 ServiceNow 来满足他们的运营需求。ServiceNow 在其基线实例中为客户端提供了额外的附加功能,因为脚本可以用来自定义和改进实例的性能。ServiceNow 提供内置的 JavaScript API 用于脚本编写和通过 JavaScript 改进实例。
本书将首先介绍 ServiceNow 脚本的基础知识以及何时在 ServiceNow 环境中编写脚本。然后,我们将深入探讨使用 JavaScript API 的客户端和服务器端脚本。我们还将涵盖诸如按需函数、脚本操作和最佳实践等高级概念。本书将作为编写、测试和调试 ServiceNow 脚本的端到端指南。我们还将涵盖用于在 ServiceNow 实例之间移动自定义设置的更新集、用于创建自定义页面的 Jelly 脚本以及 ServiceNow 中所有类型脚本的最佳实践。
在本书结束时,您将获得使用内置 JavaScript API 在 ServiceNow 中编写脚本的实践经验。
本书面向的对象
本书的目标读者是 ServiceNow 管理员或任何愿意学习用于脚本编写和自定义 ServiceNow 实例的内置 JavaScript API 的利益相关者。拥有使用 ServiceNow 的经验是强制性的。
本书涵盖的内容
第一章,入门,介绍了 ServiceNow 脚本的基础知识。也就是说,何时以及为什么通过脚本开发自定义功能是合适的。它还介绍了何时进行配置和自定义。
第二章,探索 ServiceNow Glide 类,为您提供了 ServiceNow 如何公开其 JavaScript API 的详细信息,这使得您能够方便地编写脚本。使用这些公开的 API,您可以执行各种数据库操作。您将探索一些常用的服务器端 glide 类和客户端 glide 类。
第三章,客户端脚本编程简介,帮助您理解 ServiceNow 的客户端脚本。您将学习关于客户端脚本和 UI 策略的概念。您将了解如何编写和测试基本的客户端脚本。您将通过一些客户端脚本的实践示例来更好地理解其功能。
第四章,高级客户端脚本编程,将帮助您理解 ServiceNow 中客户端脚本的更高级方面。您将学习关于 AJAX 调用和 UI 操作的知识。您将通过一些客户端脚本的高级实践示例来更好地理解其功能。
第五章,服务器端脚本简介,涵盖了 ServiceNow 中服务器端脚本的详细信息。这将帮助您深入理解业务规则、UI 操作和访问控制的概念。您还将学习如何编写和测试服务器端脚本。您将通过一些服务器端脚本的实际示例来更好地理解其功能。
第六章,高级服务器端脚本,将涵盖 ServiceNow 中服务器端脚本的高级方面。它将帮助您理解脚本包含、后台脚本、工作流脚本和计划任务的概念。您将通过一些高级服务器端脚本的实际示例来理解脚本调用、系统调度程序和系统中的排队事件。
第七章,自定义页面简介,向您介绍了 Jelly。这将为您揭示 Jelly 在 ServiceNow 中的应用。您还将学习如何使用 Jelly 脚本用 JavaScript 创建 UI 页面。
第八章,使用 Jelly 脚本,进一步扩展了 Jelly 的知识。这将进一步揭示 Jelly 脚本在 ServiceNow 中的应用。您还将学习如何创建 UI 宏以增强 UI 页面。
第九章,调试脚本,介绍了在 ServiceNow 中调试脚本的方法。您将探索在 ServiceNow 中用于故障排除和调试代码的各种工具和方法。
第十章,最佳实践,涵盖了开发人员应遵循的各种最佳实践,以高效地使用 ServiceNow。它还讨论了记录和监控系统性能以控制 ServiceNow 环境。
第十一章,使用更新集进行部署,指导您如何将配置和自定义设置从实例迁移到实例。它还帮助您了解在处理全局和范围应用时如何使用更新集。您还将学习如何在处理更新集时避免一些常见陷阱。
第十二章,使用 ServiceNow 脚本构建自定义应用程序,为您提供了 ServiceNow 中脚本的端到端实现。您将学习如何使用 ServiceNow 提供的脚本构建自定义应用程序。
为了充分利用这本书,
在开始学习 ServiceNow 脚本精通之前,建议您对 ServiceNow 平台有一个良好的理解。建议您拥有系统管理员认证或类似知识,并了解 ServiceNow 中的表单、列表和表格。您还应能够轻松地在 ServiceNow 实例中导航。
对 JavaScript 有一些了解将很有帮助,但不是强制性的,因为提供的示例将为您提供尝试并开始编写的脚本。
建议您拥有一个 ServiceNow 实例,以便尝试示例并创建一些自己的脚本。如果您目前没有实例,ServiceNow 可以为希望提高 ServiceNow 技能的用户提供个人开发者实例(PDI)。
下载示例代码文件
您可以从www.packtpub.com的账户下载本书的示例代码文件。如果您在其他地方购买了此书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。
您可以通过以下步骤下载代码文件:
-
在www.packtpub.com上登录或注册。
-
选择“支持”选项卡。
-
点击“代码下载与勘误”。
-
在搜索框中输入书名,并遵循屏幕上的说明。
文件下载完成后,请确保使用最新版本的以下工具解压或提取文件夹:
-
WinRAR/7-Zip for Windows
-
Zipeg/iZip/UnRarX for Mac
-
7-Zip/PeaZip for Linux
本书代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/Mastering-ServiceNow-Scripting。如果代码有更新,它将在现有的 GitHub 仓库中更新。
我们还有其他来自我们丰富图书和视频目录的代码包,可在github.com/PacktPublishing/上找到。查看它们吧!
下载彩色图像
我们还提供包含本书中使用的截图/图表彩色图像的 PDF 文件。您可以从这里下载:www.packtpub.com/sites/default/files/downloads/MasteringServiceNowScripting_ColorImages.pdf。
使用的约定
本书使用了多种文本约定。
CodeInText:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“onLoad客户端脚本类型在表单加载时运行脚本。”
代码块设置如下:
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return;
}
//Type appropriate comment here, and begin script below
}
粗体:表示新术语、重要单词或您在屏幕上看到的单词。例如,菜单或对话框中的单词在文本中显示如下。以下是一个示例:“要访问工作室,请导航到系统应用 | 工作室。”
警告或重要注意事项看起来像这样。
小贴士和技巧看起来像这样。
联系我们
我们欢迎读者反馈。
一般反馈:请通过feedback@packtpub.com发送电子邮件,并在邮件主题中提及书籍标题。如果您对本书的任何方面有疑问,请通过questions@packtpub.com发送电子邮件给我们。
勘误:尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们将非常感激您能向我们报告。请访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入详细信息。
盗版:如果您在互联网上发现我们作品的任何非法副本,我们将非常感激您能提供位置地址或网站名称。请通过copyright@packtpub.com与我们联系,并附上材料的链接。
如果您有兴趣成为作者:如果您在某个领域有专业知识,并且有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com。
评论
请留下评论。一旦您阅读并使用过这本书,为何不在您购买它的网站上留下评论?潜在读者可以查看并使用您的客观意见来做出购买决定,Packt 公司可以了解您对我们产品的看法,我们的作者也可以看到他们对书籍的反馈。谢谢!
如需更多关于 Packt 的信息,请访问 packtpub.com。
免责声明
本书提供的代码仅供开发场景使用,以进一步了解 ServiceNow 脚本。使用本书提供的代码由读者自行决定。作者和出版社不对任何由此书中的代码引起的损害或负面影响承担责任,也不接受任何责任。
第一章:入门指南
欢迎您。在这本书中,我希望教会您并提高您的 ServiceNow 脚本技能,以确保您真正成为一名脚本大师。我将展示何时使用脚本配置和定制 ServiceNow 以实现您的目标。基于 ServiceNow 的 glide 类,还将探讨并解释客户端和服务器端脚本。
通过 Jelly 脚本和定制页面进行深入,本书还将探讨调试和最佳实践,最后介绍如何构建您自己的定制应用程序。
在本章的开头,我们将探讨何时适合编写脚本,以及为什么应该使用脚本,以及如何配置或定制 ServiceNow 实例。我们还将探讨 ServiceNow 中的基本脚本类型及其运行时机。本章还将涵盖 ServiceNow 脚本的运行顺序、脚本简介以及用于脚本的脚本编辑器。
本章我们将涵盖的主题包括:
-
配置与定制
-
在 ServiceNow 中何时编写脚本
-
脚本类型
-
ServiceNow 中脚本的执行顺序
-
脚本基础知识
-
脚本编辑器
配置与定制
ServiceNow 实例很复杂。它们可以通过许多方式改变,而且通常,使用不同的技术实现相同目标的方法有很多。这些更改可以通过配置和定制来完成。
配置使用 ServiceNow 界面来设置规则、条件和其他配置,如全局系统属性和过滤器。这通常是通过一系列下拉列表来完成的。
我们可以在以下业务规则示例中看到这一点:

图 1.1:来自业务规则的配置示例
在 图 1.1 中,我们可以看到筛选条件和基于筛选设置值的条件。此配置示例不使用代码,并且比定制更可取。这种筛选类型在 ServiceNow 平台上随处可见。
大多数系统管理员将能够仅通过配置来管理他们的实例。配置后的实例可以在实例上满足一定级别的功能,如果您试图实现配置可用的目标,这通常是最佳选择。
定制是指管理员使用脚本允许实例执行配置本身无法单独完成的进一步任务。ServiceNow 非常开放,在加载和提交表单时在多个点编写脚本的能力使其非常灵活。
一种定制可能是编写一个脚本,在字段上添加日期验证,并在输入错误值时显示消息或清除字段。定制脚本应由创建者管理,因为 ServiceNow 不负责代码。因此,如果您开始进入定制路线,请确保您知道自己在做什么。
由于创建者需要维护创建的脚本,他们还需要确保脚本在 ServiceNow 实例升级后仍然能够运行。
如果要自定义现有的 ServiceNow 脚本,而不是直接自定义脚本本身,请复制它,重命名复制的脚本,并停用现有的脚本。然后你可以对新的复制脚本进行任何你喜欢的更改,同时保留原始脚本以防万一需要。
要编写脚本还是不要编写脚本?
对于新管理员来说,知道何时编写脚本可能很困难。对于程序员来说,由于在 ServiceNow 中有许多编写脚本的机会,所以在需要之前就编写脚本可能很容易。然而,尽可能不编写脚本会更好。
如果你可以通过配置而不是编写脚本来实现,那么这应该是你的首要考虑。这允许工具以预期的方式使用,并在升级实例时让你准备得更充分。不仅如此;使用配置而不是自定义的主要原因之一是为了维护。配置可以由不同的管理员轻松维护,而自定义则需要一些对脚本及其逻辑的基本了解,因此它们往往更难维护和调试。记住,一旦开始自定义,维护该自定义就是你的责任。
如果你发现自己仅用配置无法实现目标,那么你应该考虑编写脚本。尽管配置是你的最佳选择,但脚本在大多数实例上提供了许多高级功能。几乎所有的成熟实例都会进行一定程度的脚本编写,但那些表现更好的实例是在适当的时候进行了脚本编写的。
例如,如果你想要显示、隐藏、使字段必填或只读,这可以通过客户端脚本或 UI 策略来完成。如果你只需要根据字段中的值执行一项操作,那么在这种情况下,UI 策略是更好的选择,因为它可以在不使用脚本的情况下实现目标。然而,如果你需要根据登录用户是否具有特定角色来执行操作,那么你需要使用脚本。脚本可以在 UI 策略中完成,但在此场景中,我通常选择客户端脚本。
尽可能避免编写脚本。配置实例而不是编写脚本有许多好处,并使实例更容易维护。
脚本类型
在 ServiceNow 中,你可以编写许多不同的脚本,它们运行的时间通常决定了哪种脚本最好。然而,所有这些脚本都将分为两大类。这些是客户端脚本和服务器端脚本。
这两种脚本类型将在后续章节中进一步探讨,但在这里我们将先看看基本定义:
-
客户端脚本将在用户面前运行,基于用户在网页上接收到的数据,通常是一个表单或列表,在这种情况下不需要表单提交。这些脚本只能使用作为网页一部分加载的数据来运行它们的脚本(如果它们仅在客户端运行),因为这是唯一可用的数据。在 ServiceNow 中,最常见的客户端脚本简单地被称为客户端脚本。这些脚本的一些常见用途包括吸引用户注意需要更改的字段或验证字段值。
-
服务器端脚本将在表单提交或发生不同触发器后幕后运行。由于此类脚本在服务器上运行,它可以使用 ServiceNow 数据库中持有的所有数据,而不仅仅是加载在网页上的数据。业务规则是最常用的服务器端脚本。业务规则有多种运行方式,但通常会在表单提交后运行,常见的任务包括修改字段值或更新父或子记录。
下表显示了最常见的脚本类型以及它们是否在客户端或服务器端运行:
| 客户端 | 服务器端 |
|---|---|
| 客户端脚本 | 业务规则 |
| 用户界面策略 | 访问控制 |
| 用户界面操作 | 脚本包含 |
| 用户界面操作 | |
| 定时作业 | |
| 背景脚本 | |
| 工作流脚本 | |
| 脚本操作 |
您可能会注意到,从前面的表中可以看出,用户界面操作出现在客户端和服务器端。这是因为它们可以运行在任一侧,因此它们适合这两个类别。我们将在后面的章节中更详细地讨论这一点以及其他常见的脚本类型。
前面的表中的所有脚本类型都将在稍后进行更详细的研究,并且每个脚本类型都在充分利用 ServiceNow 实例中扮演着重要的角色。
在可能的情况下,服务器端脚本被认为是首选的,因为它们可以在用户之外的后台运行,而客户端脚本则在用户面前运行,并且通常会导致页面加载延迟。
客户端脚本可以调用服务器端脚本。这通常会导致轻微的延迟,因为信息是从数据库中收集的。在可能的情况下,最好避免从客户端脚本中进行此类服务器调用,但通常这是必要的。稍后,我们将讨论如何从客户端最佳地调用服务器端脚本,而不会为用户造成长时间的延迟。
客户端和服务器端脚本是 ServiceNow 脚本的重要组成部分,您如何利用它们来获得优势将决定您在 ServiceNow 脚本中的整体成功。
当编写新脚本时,请考虑结果是否需要立即显示在用户面前。如果不是,考虑使用服务器端脚本而不是客户端脚本。
脚本执行
在 ServiceNow 中脚本执行的顺序可能非常重要。后续运行的脚本可能会撤销或更改先前脚本所做的更改。
一些脚本可以由管理员指定顺序,而另一些则不能。了解如何对脚本进行排序将如何影响脚本的结果是很重要的。对于不能排序的脚本,它需要在与其他基线或自定义脚本相比的任何顺序中运行,并且仍然能够正确执行。
当脚本可以排序时,它们将根据分配给它们的顺序号执行。脚本按顺序号升序执行,因此顺序号为 50 的脚本将先于顺序号为 100 的脚本执行。然而,每个数字并不是唯一的,因此您可以在顺序号为 100 的情况下运行多个脚本,这是新脚本的默认设置。在这种情况下,对于具有相同顺序号的脚本,您不能确定它们的执行顺序。
考虑一个场景,其中脚本 A 已经存在。假设脚本 A 将用户的活跃字段设置为 true。作为管理员,我编写脚本 B 将用户的活跃字段设置为 false。如果脚本 B 的顺序低于脚本 A,它将没有效果。这很容易被误解为脚本 B 没有正确工作,但实际上它只是被脚本 A 覆盖了。如果脚本 B 的顺序高于脚本 A,那么脚本 B 看起来会正常工作。然而,脚本 A 就变得冗余了。
两个脚本如此明显地相互对立的情况并不常见,但在其他代码和多个脚本中隐藏的上述场景在更复杂的背景下相当常见。因此,排序非常重要,并且是脚本问题的一个常见原因。为了确保您在更改自己的脚本时不会遇到排序问题,确保在更改之前了解您正在处理的字段或字段上的其他脚本。
如前所述,有时您无法指定脚本的执行顺序。一个例子是客户端脚本。这些脚本将基本上以随机顺序执行,因此,在编写它们时,管理员需要考虑到这一点。这意味着您不能编写依赖于另一个客户端脚本中的值或字段的客户端脚本,否则它可能会出错(如果执行顺序不是您所期望的)。
然而,大多数服务器端代码是可以排序的。服务器端脚本的执行顺序可以在以下图中看到:

图 1.2:脚本的执行顺序
正如您在前面的图中看到的,脚本在多个不同的时间点运行,因此,对于管理员来说,确定脚本的正确顺序是很重要的。
从这个排序系统中需要注意两个重要点。第一个是,除了电子邮件通知外,脚本可以在数据库操作之前或之后调用。选择正确的脚本执行时机可以帮助简化实例。选择错误的执行时间通常不会引起问题,但可能会相当低效。通常,如果脚本将更改当前记录的值,则最好在数据库操作之前调用脚本;否则,脚本可以在数据库操作之后运行。
第二个重要点是,在 ServiceNow 中,1000 这个顺序号是一个重要的数字。当我第一次开始使用 ServiceNow 时,我很好奇为什么有这么多脚本的顺序号如此之高。原因是运行脚本在引擎之前或之后。这些引擎包括 SLA、审批和工作流引擎。我在经验中遇到的主要原因是根据工作流脚本来排序脚本。
脚本执行顺序错误可能难以诊断,因为它可能需要花费很长时间来找出哪些其他脚本正在干扰你正在处理的当前脚本。这就是为什么在脚本中清晰地标记所有代码并添加有意义的注释很重要的原因。
脚本简介
ServiceNow 使用 JavaScript 作为编写或编辑的大多数脚本的编程语言。如果你已经具备 JavaScript 的背景,那么在编写第一个脚本时你肯定会有一个先发优势。用于编写脚本的 JavaScript 引擎会随着 ServiceNow 新版本的发布而更新。另一种使用的语言称为 Jelly。在 ServiceNow 领域之外,很少有人知道 Jelly,而且在我与 ServiceNow 合作之前,我当然不熟悉这种语言。幸运的是,Jelly 在大多数脚本中并不常用,我们将在后面的章节中介绍如何以及在哪里使用它。
在开始编写你的第一个脚本之前,检查是否可以在不使用脚本的情况下实现你的目标是很重要的。这应该在你开始之前始终是一个考虑因素。例如,通过利用 UI 策略形式的配置,可以完全避免定制。
在编写脚本时,重要的是要考虑你想要实现什么,以及你需要访问数据库中的哪些记录和字段。最常见的脚本只需要从当前显示或更新的记录中获取信息,但更复杂的脚本可能需要访问来自几个不同表的数据。在开始之前确保数据库和表结构允许你从当前记录中获取脚本所需的所有数据是一个好习惯。
在编写脚本时,还有一个需要注意的要点是确保您的代码有良好的文档记录,包括良好的注释和对要实现的目标的描述。当您编写代码时,可能会觉得它所做的是显而易见的,但经过一段时间再次回到相同的代码时,情况往往并非如此。维护 ServiceNow 实例是管理员工作的一部分重要内容,在没有注释的脚本中尝试这样做可能是一项繁重的工作。我遇到过许多注释和描述不足的实例,最初在发现它们是如何相互关联上浪费了很多时间。记录您的代码不仅会随着时间的推移帮助您,还会帮助任何需要维护您的脚本和实例的其他管理员。
例如,解释每个函数的功能是很重要的:
/*This function returns true or false based on the whether the input variable is 0
Inputs: input - integer
*/
function myFunction(input) {
if (input == 0) {
return true;
} else {
return false;
}
}
大多数脚本将使用 ServiceNow 提供的 glide 类,我们将在下一章中讨论。
脚本编辑器
当您在 ServiceNow 中遇到脚本字段时,它将具有一些额外的功能,这些功能允许您比在简单文本字段中更容易地创建脚本。这种类型的字段被称为脚本类型,并在 ServiceNow 中随处可见。
我们现在将详细查看此字段类型,以便您可以使用此字段发挥其最佳效果并了解其功能。首先,让我们看看 图 1.2 中的此字段:

图 1.3:脚本类型字段
字段上方有许多按钮,您可以使用这些按钮来发挥优势。让我们依次查看这些按钮,因为每个按钮都可以帮助我们编写代码:
-
滚动: 滚动允许我们打开或关闭语法高亮和脚本格式化。默认情况下是开启的,大多数人会发现这是更好的选项。然而,如果您更愿意禁用此功能并偏好使用简单文本字段编写,请将此按钮切换到关闭。如果切换到关闭,所有其他按钮都会消失,只留下一个按钮,允许您转到特定的代码行。保持开启状态,这将允许脚本字段中的脚本进行颜色编码,通过间距格式化以使其更容易阅读,并显示语法错误。语法错误将出现在左侧的边框上,您可以将鼠标悬停在其上以查看问题。
-
气泡: 气泡切换当前脚本行是否被注释。许多开发者会直接编辑脚本字段本身,但这对多行来说可能很有用。
-
对齐文本: 此按钮格式化脚本字段中的代码。它会在 if 语句和循环中缩进脚本,使代码更容易阅读。这是一个我发现非常有用的按钮。
-
替换字母: 此按钮允许您将脚本中的单词或短语替换为不同的文本。这是针对您找到的每个单词或短语实例进行的,因此您可以替换许多或少数单词或短语。
-
替换字母为文本: 此图标类似于替换按钮,但它将替换脚本字段中所有实例的单词或短语,而不会单独通知您每个实例。因此,在使用此图标之前,请确保您想要替换脚本字段中的每个特定单词或短语。
-
放大镜: 通过点击此按钮,我们可以在脚本字段中搜索单词或短语。
-
向下箭头: 在执行搜索后,这允许用户看到搜索到的单词或短语的下一个实例。
-
向上箭头: 一旦在脚本字段中执行了搜索,这将显示之前在脚本字段中搜索过的单词或短语。
-
框形箭头: 这可以在全屏和屏幕上的较小框之间切换脚本字段。当处理更大的脚本时,这可以很有帮助,以避免过度滚动。
-
问号: 点击此按钮会显示脚本编辑器和您可以使用的快捷键的弹出摘要。
-
磁盘: 这将保存记录和脚本。
-
勾选滚动条: 这可以切换语法检查的开启和关闭。当发生这种情况时,您会注意到左侧边距大小的变化。通常,语法检查是有用的,但这确实提供了隐藏它的选项,如果某些语法建议不适合您。
-
滚动错误: 通过选择此图标,脚本调试器将在新窗口中打开。
一些开发者将使用他们自己的外部脚本工具来创建脚本,并在完成后将其粘贴到脚本字段中。如果这样做,请确保您的外部工具中代码的版本是最新的,以免不小心覆盖他人的更新。
这提供了此类字段的总览,并使脚本编写变得更加容易。语法检查和代码格式化是特别有用的工具。
摘要
在本章中,我们讨论了 ServiceNow 实例的配置和自定义,以及何时应该同时使用它们。我们还介绍了两种脚本类型:客户端和服务器端脚本。本章还解释了 ServiceNow 中脚本的运行顺序、顺序 1000 的重要性以及它如何影响脚本结果。我们介绍了准备编写脚本,包括在开始之前的一些考虑;最后,我们探讨了脚本编辑器。
在下一章中,我们将探讨 ServiceNow 的 glide 类。这将向您展示 ServiceNow 提供的脚本,这些脚本可以用来补充您的 JavaScript。glide 类是 ServiceNow 脚本中常用的一个方面,了解其使用方法将使您能够编写有用的基本脚本,并作为编写更高级代码的绝佳第一步。
第二章:探索 ServiceNow Glide 类
在本章中,我们将探索 ServiceNow Glide 类。我们将查看允许开发者方便且快速编写 ServiceNow 脚本的公开 JavaScript API。然后,我们将查看服务器和客户端上的不同类,最后查看一些常用类的示例。
本章将涵盖以下主题:
-
如何使用 ServiceNow 公开的 JavaScript API,包括 GlideRecord
-
客户端 Glide 类
-
服务器端 Glide 类
-
涵盖一些常用 Glide 类的示例
使用 ServiceNow 公开的 JavaScript API
ServiceNow 为开发者提供了一些公开的 JavaScript API,以帮助他们编写脚本。这大大节省了在 ServiceNow 脚本中访问记录和字段的时间。开发者可以使用许多不同的类和对象,其中一些非常有用,而另一些则很少使用。
GlideRecord
最常见的 JavaScript API 之一是 GlideRecord 类,它非常实用,很快将成为大多数脚本的必备工具。GlideRecord 是在 ServiceNow 中根据许多不同的查询查找和计数记录的一种方式。如果你熟悉 SQL 语句,它相当类似。
让我们看看如何使用 GlideRecord。
我们将看看如何查询特定表中的所有记录。此 GlideRecord 脚本的格式如下所示,其中 table_name 是唯一的参数。这需要是表名而不是表标签:
new GlideRecord('<table_name>');
我们将设置一个变量来保存 GlideRecord 对象,并定义将用于查询的表。在这个例子中,我们将使用事件表:
var glideRecord = new GlideRecord('incident');
glideRecord 变量现在保存了事件表的 GlideRecord 对象。然后我们查询表并添加任何过滤。在这种情况下,我们将添加没有过滤,仅返回事件表的所有记录,因此显示完整的脚本:
var glideRecord = new GlideRecord('incident');
glideRecord.query();
while (glideRecord.next()) {
//Code in this loop will be run against all incident records
}
这是 ServiceNow 脚本中的一个非常重要的部分,因为这是编码者通过遍历它们并应用一些代码到每个相关记录的方式来访问 ServiceNow 中的记录的方式。
现在我们已经看到了如何设置 GlideRecord 查询,让我们看看可以为我们过滤 GlideRecord 的脚本行:
variable.addQuery('<field_name>', '<operator>', '<value_to_compare_against_field>')
在这里,我们根据字段名称进行过滤,并使用运算符将那个字段中的值与我们定义的值进行比较。默认的 operator 是值等于字段中的值,所以如果这是我们需要的运算符,我们就可以省略它。
让我们看看另一个例子。这次,我们将使用过滤行查询所有具有关键优先级的问题记录:
var glideRecord = new GlideRecord('problem');
glideRecord.addQuery('priority', 1);
glideRecord.query();
while (glideRecord.next()) {
//Code in this loop will be run against all problem records with a priority of critical.
}
如前述代码所示,我们省略了运算符,因为我们想要具有优先级等于 1 的问题记录,因此我们不需要包含它。
在之前的示例中,我们已经遍历了所有记录,但我们可以通过将 while 改为 if 来修改循环,以便只返回一个存在的记录。这在检查父记录的所有任务是否完成时非常有帮助。让我们看看它将如何改变我们上一个示例中的脚本:
var glideRecord = new GlideRecord('problem');
glideRecord.addQuery('priority', 1);
glideRecord.query();
if (glideRecord.next()) {
//Code in this loop will be run against one problem record with a priority of 1 if one exists.
}
这段代码可以用来检查是否存在任何优先级关键的问题,如果存在,将会对它运行一些代码。
你可以添加多个查询,并且每个返回的记录都需要满足定义的每个过滤器。在这方面,它很像寻找房子。你可以只搜索所有的房子,但你更有可能想要建立一系列过滤器来找到你确切想要的东西——价格范围、卧室数量、位置等等。
由于所有这些过滤器累加起来,本质上创建了一系列逻辑 AND 语句,我们还需要一种创建逻辑 OR 语句的方法。同样地,你可以在查询行中添加,也可以添加一个脚本中的 OR 条件行。让我们看看它是如何工作的:
var glideRecord = new GlideRecord('change_request');
var orQuery = glideRecord.addQuery('risk', 1);
orQuery.addOrCondition('risk', 2);
glideRecord.query();
while (glideRecord.next()) {
//Code in this loop will be run against all change request records that are a very high or high risk.
}
如您所见,原始条件存储在一个变量中,orQuery,然后在查询之前将 OR 条件添加到这个变量中。如果你想在记录处于某些状态时执行某些代码,这种查询类型非常方便。
在 ServiceNow 中实现相同目标的方法有很多,为了展示这一点,我们可以使用不同的运算符在更改记录上设置相同的过滤器。这是基于 ServiceNow 中选择列表的许多值都是数字的事实,因此我们可以搜索所有风险大于或等于 2 的更改请求:
var glideRecord = new GlideRecord('change_request');
glideRecord.addQuery('risk', '>=', 2);
glideRecord.query();
while (glideRecord.next()) {
//Code in this loop will be run against all change request records that are a very high or high risk.
}
第二个示例通常被认为比第一个更好,因为它使用了更少的代码行。
现在我们已经了解了如何获取我们想要的记录,我们将探讨如何修改我们找到的记录。首先,我们将查看对记录的简单更新。在这个例子中,我们将更新所有紧急程度高的突发事件记录,将其降低到中等:
var glideRecord = new GlideRecord('incident');
glideRecord.addQuery('urgency', 1);
glideRecord.query();
while (glideRecord.next()) {
//Change all high urgency incidents to medium urgency
glideRecord.urgency = 2;
glideRecord.update();
}
当更新一条记录时,你可以修改许多字段,然后使用 update 来保存更改。这是一个非常重要的方法,并且相对简单易用。
我们将要查看的最后一个示例是关于如何删除记录。显然,在删除记录时要非常小心,并且在执行之前确保你编写的查询是正确的。
删除记录有两种主要方法:要么通过循环逐个删除,要么一次性全部删除。让我们先看看如何逐个删除记录。在这个例子中,我们将删除所有 network 类别的突发事件:
var glideRecord = new GlideRecord('incident');
glideRecord.addQuery('category', 'network');
glideRecord.query();
while (glideRecord.next()) {
glideRecord.deleteRecord();
}
接下来,我们将探讨如何一次性删除所有查询到的记录:
var glideRecord = new GlideRecord('incident');
glideRecord.addQuery('category', 'network');
glideRecord.deleteMultiple();
如您所见,这两个 delete 方法的脚本相当不同,但最终结果相同。deleteMultiple 方法是一种更快、更有效的方法;然而,它将一次性删除所有内容。如果需要删除大量数据,这可能会导致资源长时间占用。在处理大量数据的情况下,我使用了 deleteRecord 方法来分批删除记录。
当创建 delete 脚本时,尝试先注释掉 delete 方法并添加一些日志来显示你已删除的内容。如果日志返回了你想要的记录,那么就可以继续删除。这有助于确保不会删除错误的记录。
至此,GlideRecord 的介绍就到这里,但它在整本书中还会出现,进一步证明它的重要性。
服务器端 Glide 类
现在我们将探讨一些我们可以用于脚本的服务器端类。请记住,这些方法在客户端不起作用,所以请确保你清楚你正在编写的脚本是在服务器端。
GlideSystem
GlideSystem 可能是使用最广泛的服务器端类。让我们看看我们如何利用这个类来帮助我们进行脚本编写。
在脚本中,ServiceNow 将 GlideSystem 缩写为 gs,因此 GlideSystem 的方法将以 gs 为前缀。
让我们先看看如何使用 GlideSystem 获取用户的 sys_id:
var userID = gs.getUserID();
这将登录用户的 sys_id 存储在 userID 变量中。这很有用,因为你可以将其用于脚本中,根据用户的属性执行不同的脚本行。现在我们有了用户的 sys_id,我们可以使用 GlideRecord 查询来返回我们想要的字段。ServiceNow 允许我们以更简单的方式获取一些信息。
我们可以获取 user 对象,然后使用一些有用的函数来获取有关用户的更多信息。要获取 user 对象,我们只需编写以下代码:
var userObject = gs.getUser();
接下来,让我们看看一些最有用的函数:
gs.getUser().getFullName();
gs.getUser().getEmail();
gs.getUser().getLocation();
gs.getUser().getManagerID();
gs.getUser().getCompanyID();
上述代码的大部分内容相当直观,但 ID 函数将以与用户 sys_id 相同的方式返回记录的 sys_id。
用户的详细信息对于发送通知和根据位置或公司设置批准和条件脚本非常有用。
当我们谈论用户数据时,了解用户拥有哪些角色通常很重要,以便决定他们应该有权访问什么。GlideSystem 允许我们在脚本中查看用户是否拥有特定的角色,如下面的示例所示:
if (gs.hasRole('admin')) {
//Run code for administrators only
}
hasRole 方法在用脚本允许或限制用户访问数据时特别有用。它还可以用于隐藏或显示需要更高权限才能使用的 UI 操作。需要注意的是,hasRole 方法对于管理员总是返回 true。
GlideSystem也适用于通过用户的输出消息让用户知道脚本中发生了什么。这是通过使用Info或Error消息来完成的。Info消息以漂亮的蓝色框显示,而Error消息,不出所料,在处理完成后和下一个屏幕加载后,在表单的顶部以红色显示。
让我们看看如何编写这些脚本:
gs.addInfoMessage('Record saved successfully.');
gs.addErrorMessage('Error in script.');
这些线条相当简单,但在保持用户了解脚本在处理过程中的表现以及是否存在任何问题时非常实用。我倾向于使用信息消息来告知用户操作已成功完成,尤其是在他们返回的屏幕上并不立即明显的情况下。当脚本中出现问题的时候,错误消息是很好的。记住,这条消息将会显示给各种用户,所以最好不要在消息中过于技术化。
GlideSystem还允许我们执行日志记录,以便我们可以调试我们的服务器端脚本。由于服务器端脚本在幕后运行,我们需要一种方法来记录脚本中发生的事情,这样我们就可以稍后查看它,并在必要时进行审查和调试。
对于这种情况,最常见的脚本片段是log。简单来说,这只是一个要发送到日志中的文本字符串:
gs.log('Logging Message');
这条消息现在将在应用程序导航器中的脚本日志语句模块中可见。如果您正在查看 ServiceNow 实例中的旧脚本,您通常会找到这些日志消息。有时它们被注释掉,以防将来需要用于调试,有时它们应该被注释掉,但开发者忘记了。
如果正在创建许多日志,给您的日志一个独特的来源可能会有所帮助,这样您就可以更容易地搜索来自您特定来源的日志。让我们看看这看起来是什么样子:
gs.log('Logging Message', 'My Script Log');
这条消息将随后出现在系统日志中,来源为My Script Log。需要注意的是,由于您已将来源更改为自定义来源,它将不再出现在脚本日志语句模块中。
GlideSystem可能是最有用的服务器端 Glide 类,并且还有更多我们讨论过的方法。
GlideDateTime
GlideDateTime显然是关于日期和时间的脚本,具体来说是GlideDateTime对象。GlideDateTime对象主要用于填充日期/时间字段及其周围的日期和时间方法,以向字段添加或减去时间。
首先,让我们看看如何定义一个新的GlideDateTime:
var glideDT = new GlideDateTime();
这将把当前的日期和时间以 GMT 格式放入glideDT字段,作为GlideDateTime对象的一部分。如果您想比较字段中的日期与当前日期和时间,这可能很有帮助。
现在,在处理任何日期和时间脚本时,时区总是一个问题。解决这个问题的最好方法之一是使用显示值,确保日期和时间以正确的格式显示给查看它的用户。让我们看看如何通过记录显示值来完成这项工作:
var glideDT = new GlideDateTime();
gs.log(glideDT.getDisplayValue());
这在脚本中非常有用,可以确保所有用户都能看到正确的时间。
有时也可能需要使用脚本从日期/时间字段中添加或删除时间。有一些方法可以将各种时间量添加到或从日期/时间字段中。我们将查看其中之一;其他的工作方式非常相似:
var glideDT = new GlideDateTime();
glideDT.addDaysLocalTime(1);
在这个例子中,我们将向当前日期和时间添加 1 天,即 24 小时。
var glideDT = new GlideDateTime();
glideDT.addDaysLocalTime(-1);
通过使用负数,我们实际上从对象中减去时间,所以前面的例子实际上会给你昨天这个时间的日期和时间。
GlideDateTime 在处理日期和时间时非常有用,这在编码中经常会导致头疼。记住,当在 addtime 方法中使用负数时,这会减少时间而不是增加它。
GlideElement
GlideElement 为处理 GlideRecord 对象中的字段提供了方法。这个类是 ServiceNow 中较小的一个。
在这个类中有一组非常方便的方法,用于检测字段的更改。这在关闭记录时非常有用。有时我们可能希望在记录关闭时运行一些脚本,但不是每次记录更新时都运行。这就是 changesTo 方法可以发挥作用的时候。我们将假设我们正在使用一个事件,在这种情况下,关闭状态是 7:
if (current.state.changesTo('7')) {
//Run some closure script
}
前面的例子将允许在记录关闭时运行一些脚本,但只有在它变为关闭状态时才会运行。如果关闭的记录随后被更新,那么这个脚本将不会再次运行。这就是为什么这个方法如此有用的原因。你还可以使用 changes 和 changesFrom 方法作为这个集合的一部分。
你可能或可能不知道此时 current 指的是什么,但当我们查看第五章“服务器端脚本简介”中的业务规则时,我们会更详细地探讨这一点。
在 GlideElement 中还有一组很有用的方法,用于检查用户是否有能力创建、读取或写入记录。我们可以使用这个方法来查看用户是否应该能够执行这些操作,这在 UI 动作条件中非常有帮助。
让我们看看它是如何工作的:
if (current.canCreate()) {
//Run some creation script for the current record type
}
这将检查登录用户是否有能力创建当前类型的记录(事件、变更请求等),如果是的话,那么就在 if 语句内部运行脚本。我们也可以以类似的方式使用 canRead 和 canWrite 方法。
由于 GlideElement 是一个较小的类,它不像其他一些服务器端类那样被广泛使用,但其中一些方法非常有帮助,尤其是在 UI 动作中。
GlideAggregate
GlideAggregate 类是 GlideRecord 的扩展,工作方式类似。区别在于 GlideRecord 通常会给你数据库对象,而 GlideAggregate 处理的是计数和数字。
我们快速了解一下 GlideAggregate 的工作原理:
var counter = new GlideAggregate('incident');
counter.addAggregate('COUNT');
counter.query();
if (counter.next()) {
var noOfIncidents = counter.getAggregate('COUNT');
}
这个第一个例子将给出数据库中事件的数量,并将其放置在 noOfIncidents 字段中。如果我们想减少返回的事件记录数,我们可以像对 GlideRecord 一样添加 addQuery 行。
除了 COUNT,我们还可以使用 SUM、MAX、MIN 和 AVG 来获取总和、最大值、最小值和平均值。
我发现 GlideAggregate 并不经常使用,但它是一种高效的计数记录的方法。
客户端 Glide 类
客户端 Glide 类用于在用户面前直接运行的脚本中。这包括操作和与表单字段及用户数据交互,以及能够调用服务器端脚本以返回数据库中的值。
GlideForm
我会说是 GlideForm 或 g_form 是最常用的客户端类。它主要用于从表单的字段中获取数据,并将值设置到这些字段中。我们还可以使用 g_form 改变这些字段的元素。
我们首先来看看如何从字段中获取和设置值:
var stateValue = g_form.getValue('state');
引号内的字符串值是该字段的数据库名称。确保在用 g_form 获取值时使用名称而不是标签。getValue 方法本质上是将字段的值放入变量中供你使用。然后你可以使用这个变量来检查其他数据或将其作为参数传递给函数:
g_form.setValue('state', '6');
setValue 方法会立即将屏幕上字段的值设置为脚本指定的值。在这个例子中,假设我们处于事件表中,状态字段将变为已解决。记住,在这里,我们使用数字 6,因为在 ServiceNow 中,这是已解决状态的选择值。
值得注意的是,此时值只会在用户面前的屏幕上发生变化。数据库中的字段只有在记录本身更新后才会更新。
使用 g_form,我们还可以改变字段本身的元素,而不仅仅是它持有的值。让我们看看如何使字段成为必填项、显示或隐藏字段,以及使字段为只读。这些操作的最佳实践是使用 UI 策略;然而,UI 策略中的条件有限,所以有时我们需要使用脚本执行这些操作。
我们首先将字段设置为 mandatory:
g_form.setMandatory('short_description', true);
此示例将短描述字段设置为 mandatory。如果我们想撤销此操作,只需在行中将 true 更改为 false。这在使用 UI 动作在记录的状态之间移动以及确保在进入下一阶段之前某些字段已填写时可能很有帮助。此更改到字段的强制状态只是临时的;如果表单重新加载,则字段将回到其原始的强制或非强制状态。
要更改字段的可见性,我们可以使用以下代码:
g_form.setDisplay('assigned_to', false);
此示例将隐藏分配到的字段,但我们可以通过在脚本行中将 false 更改为 true 来再次显示它。这个方法并不常用,因为这个动作通常可以通过 UI 策略来完成。此方法将允许其他字段回收该字段留下的空间。setVisible 方法与 setDisplay 方法非常相似,除了在字段曾经存在的地方留下一个空白空间,这往往使 setDisplay 成为更好的美学选择。
将字段设置为只读的方法如下:
g_form.setReadOnly('description', true);
上述示例将描述字段设置为只读,这意味着字段不能被编辑。这将在当前表单加载期间发生,并在表单重新加载时恢复到其原始的只读或非只读状态。
g_form 也可以为你提供一些有用的信息。首先,让我们看看如何使用 g_form 获取记录的唯一值或 sys_id:
var sysID = g_form.getUniqueValue();
此示例将记录的 sys_id 放入 sysID 变量中。如果你想在记录保存之前就获取 sys_id 值,这可能会很有用。
我们还可以使用代码来检查记录是否已保存:
if (g_form.isNewRecord()) {
//Run script only for new records
}
isNewRecord 方法允许我们只为新记录或已保存的记录编写脚本。这可以帮助你决定在脚本末尾是插入还是更新记录。
GlideUser
GlideUser 或 g_user 是一个关于用户属性的类。它不是最大的类,但其中一些属性和方法可以相当频繁地使用。
其中最有帮助的是能够通过属性获取用户 ID,即用户的 sys_id。这可以非常有帮助,可以发送到另一个脚本,以便脚本知道当前登录的是哪个用户,或者用于获取用户记录。
让我们看看如何获取这个 ID:
var userSys_ID = g_user.userID;
在示例中,userSys_ID 变量将是登录用户的 sys_id。
在获取用户记录时,最佳实践是使用 sys_id 而不是名称来获取记录。这是因为数据库中的用户可能有相同的名称,而用户的 sys_id 将始终是唯一的。
我们也可以使用 g_user 来获取已登录用户的完整姓名。如果我们想在显示给用户的消息或字段中显示用户姓名,这可能会很有帮助。
我们可以通过以下示例获取用户的完整姓名:
var name = g_user.getFullName();
alert('The logged in user is ' + name);
在前面的示例中,我们将名称变量设置为用户的完整姓名。让我们假设我是这里的登录用户。alert方法向用户显示一个弹出消息,所以在这种情况下,它会说“登录用户是 Andrew Kindred”。
有时在编写脚本时,我们想知道用户是否具有特定的角色,以便我们可以决定是否为该用户运行代码。要在客户端执行此操作,我们可以使用hasRole方法。它的工作方式与服务器端的GlideSystem方法非常相似。
让我们看看hasRole方法的示例:
if (g_user.hasRole('itil') {
//Run code for user with the itil role only
}
如您所见,此代码与服务器端代码非常相似。我们可以使用if语句来确保只为具有特定角色的用户运行某些代码。
与基本用户数据和角色相关的一些其他方法,但我们在这里已经涵盖了最有用的方法。
额外的客户端 Glide 类
我们到目前为止看到的两个类是最有用且最容易开始的。然而,还有一些其他有用的客户端 Glide 类。我们将在本书的后面部分查看这些其他 Glide 类,因为它们与客户端脚本的一些类型和您可能希望用代码实现的具体操作很好地结合在一起。
脚本示例
现在我们已经了解了服务器端和客户端的 Glide 类,我们可以看看如何在一些稍微复杂一点的代码块中使用这些方法和属性来实现 ServiceNow 中的目标。
让我们先看看一些GlideRecord的示例。
这次,我们将多次使用GlideRecord,一个在另一个内部。随着您在脚本编写中不断进步,您无疑会经常使用这种技术。它确实在我多年的工作中发挥了很好的作用。
在这个示例中,我们将查看为每个关键优先级事件创建问题记录:
var incRec = new GlideRecord('incident');
incRec.addQuery('priority', 1);
incRec.query();
while (incRec.next()) {
//Critical incident found, create a new problem record
var newProblemRec = new GlideRecord('problem');
newProblemRec.cmdb_ci = incRec.cmdb_ci;
newProblemRec.short_description = incRec.short_description;
var newInsertedRecord = newProblemRec.insert();
//Update the incident with the problem reference
incRec.problem_id = newInsertedRecord;
incRec.update();
}
在前面的示例中,我们使用GlideRecord查询找到了所有关键事件。一旦我们找到一个匹配的事件记录,我们就使用insert创建一个新的问题记录,并将配置项和简短描述字段从事件复制到问题记录中。一旦问题记录被插入,我们就将其唯一值存储在newInsertedRecord变量中,以便我们可以将该值添加到事件记录相关的问题字段中。这确保了两个记录之间的链接,并且事件将出现在问题记录的相关列表中。
在以下屏幕截图中,我们可以看到这个脚本在计划任务中的样子:

图 2.1:从关键事件创建问题的计划任务
我们将在第六章“高级服务器端脚本”中深入探讨计划任务。此图为您提供了在 ServiceNow 脚本字段中查看 Glide 类布局的思路。
在 GlideRecord 内部使用 GlideRecord 非常有用,因为我们可以在 while 循环中创建、更新或搜索 GlideRecord 查询。
如果我们知道记录的 sys_id,可以使用一个名为 get 的快捷方式直接获取记录。我们可以使用基于用户的方法来展示获取用户记录的快速方式。
以下脚本使用此 get 方法快速访问所需的用户记录:
var userRec = new GlideRecord('sys_user');
userRec.get(gs.getUserID());
userRec.title = 'Manager';
userRec.update();
使用 get 方法,我们快速检索了当前登录用户的用户记录,同时使用我们的 GlideSystem 方法获取用户 ID。我们也可以在这里为 get 方法参数放置一个包含引号的 sys_id。然后示例将用户的标题设置为经理并保存记录。运行此代码将使任何登录用户都将经理作为他们的标题。
这种技术使我们不必使用完整的 GlideRecord 查询来获取所需的用户记录。这意味着不必搜索用户表,这节省了资源,也减少了额外的代码行。
我们可以在 ServiceNow 的业务规则中看到此示例代码的样子:

图 2.2:使登录用户成为经理的业务规则
我们将在第五章 “服务器端脚本简介” 中深入探讨业务规则。它们通常用于服务器端脚本。我们还需要向表字段添加一个值,以便我们的业务规则知道针对哪个表运行此脚本。
我们还可以使用本章中学到的一些技术向用户发送特定的消息。假设我们想要根据用户拥有的角色向用户提供不同的消息:
if (gs.hasRole('admin')) {
gs.addErrorMessage('Error with solaris server');
} else if (gs.hasRole('itil')) {
gs.addErrorMessage('Server error');
} else {
gs.addErrorMessage('An error has occurred, please contact your administrator');
}
此服务器端示例将根据用户拥有的角色向用户显示错误消息。这可以用于向理解它的用户显示附加信息,同时为技术知识较少的用户保持细节简单。
此代码在表单加载到屏幕顶部时向用户显示错误消息。通过使用 if 语句,我们只会向用户显示一条消息,在我们的示例中,这是最适合该用户角色的消息。
我们还可以根据表单是否为新表单来设置一些值。也许我们想要使所有新的事件表单都具有询问/帮助类别,但不要更改已存在的任何事件。
我们可以如下看到实现此目的所需的脚本:
if (g_form.isNewRecord()) {
g_form.setValue('category', 'inquiry');
}
使用此客户端脚本示例,如果这是一个新记录,我们只会将类别字段设置为 inquiry/help。请记住,这只会设置客户端屏幕上的字段,而不会将字段保存为该值,直到记录被保存。
这种类型的代码在创建记录时以特定方式设置记录非常有用。有时可能是在创建新记录时字段不会显示,只有在记录创建后才会查看。
我们可以在以下屏幕截图中的客户端脚本中看到此代码:

图 2.3:设置事件类别的客户端脚本
我们将在下一章进一步探讨客户端脚本,它们通常是实例中客户端脚本的主要来源。
如果我们只想在记录不是新记录时运行脚本,我们可以在if条件前放置一个感叹号来否定表达式,并给我们添加更新现有记录代码的选项。
if语句将看起来像这样:
if (!g_form.isNewRecord()) {
}
如您所见,我们可以将本章中看到的一些方法结合起来,以实现我们寻找的脚本目标。
摘要
在本章中,我们探讨了使用公开的 JavaScript API 和常用的GlideRecord类。我们还探讨了服务器端和客户端的 Glide 类以及它们各自的属性和方法,这些属性和方法可以用来增强你的脚本。最后,我们查看了一些更复杂的脚本示例,结合这些属性和方法来展示可以实现什么,以及如何一起使用这些属性和方法。
在下一章中,我们将探讨客户端脚本的基础知识。我们将探讨客户端脚本、UI 策略、何时使用这些脚本以及如何测试它们。此外,我们还将添加一些有用的示例,展示如何充分利用客户端脚本。
第三章:客户端脚本简介
在本章中,我们将开始探讨客户端基础知识。这将帮助您了解在哪里以及如何编写您的客户端脚本,以及如何测试它们。我们还将查看一些在这个领域入门的示例。
在本章中,我们将探讨以下主题:
-
客户端脚本
-
UI 策略
-
如何以及何时编写客户端脚本
-
测试客户端脚本
-
基本客户端脚本示例
客户端脚本
客户端脚本通常是开发者最常使用的客户端脚本。默认情况下,客户端脚本将在表的所有视图中运行,但可以通过取消选中全局复选框并将特定视图输入到出现的视图字段中来设置为仅针对某个视图运行。客户端脚本还可以继承到从不同表扩展的表中。例如,任务表上的客户端脚本可以继承到扩展它的表中,如事件、变更和问题表。要允许表继承客户端脚本,必须设置继承复选框。
客户端脚本中的脚本可以在开发者选择的不同时间运行。这种选择是通过为客户端脚本选择一个类型来实现的。
客户端脚本有四种类型,它们是:
-
onLoad -
onChange -
onSubmit -
onCellEdit
让我们逐一查看每种类型,以及您何时可能想要使用每种类型来实现您的脚本目标。
onLoad
onLoad客户端脚本类型在表加载时运行脚本。这有助于在用户开始使用表之前操纵表中的数据。有时,当表加载缓慢时,可以看到onLoad客户端脚本正在执行其任务。
当选择onLoad类型时,只要脚本字段尚未被编辑,脚本字段将填充 ServiceNow 为您提供的onLoad函数。脚本如下所示:
function onLoad() {
//Type appropriate comment here, and begin script below
}
这是将您的脚本放入的onLoad函数。任何运行onLoad的脚本都会花费时间运行并减慢加载时间,因此始终值得尝试将onLoad客户端脚本保持在最小,如果需要,则尽可能简短。我曾参与过仅为了减少表上onLoad客户端脚本的数量以减少加载时间的项目。
尽量让每个表只有一个onLoad客户端脚本,而不是多个。这样便于维护,因为所有onLoad代码都在一个地方,您可以完全确信onLoad代码将按什么顺序运行。
在以下图中,图 3.1,我们可以看到这种类型客户端脚本的外观:

图 3.1:示例 onLoad 客户端脚本
在前面的图中,我们可以看到一个为事件表编写的onLoad客户端脚本。正如代码中的注释所述,将代码中的注释替换为脚本的功能,然后在onLoad函数内的注释下方开始编写。
onChange
onChange 客户端脚本在表单上更改选定的字段时运行。重要的是要注意,当表单加载时,onChange 脚本也会运行。这种类型的脚本通常用于根据 onChange 脚本运行的字段中的数据自动填充表单上的其他字段。例如,如果用户在表单上被选中,表单上的其他用户数据,如公司和职位,可以使用 onChange 脚本来填充。
与 ServiceNow 在 onLoad 脚本中提供脚本函数的方式相同,onChange 函数也被提供。这稍微复杂一些,让我们看看:
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return;
}
//Type appropriate comment here, and begin script below
}
onChange 函数为我们提供了五个参数,我们可以在脚本函数内部使用这些参数。以下是每个参数提供的内容:
-
control:已更改字段的动态 HTML -
oldValue:表单加载时更改字段的值 -
newValue:已输入到更改字段的新的值 -
isLoading:如果表单正在加载,则为真;否则为假 -
isTemplate:如果更改是作为模板加载的一部分发生的,则为真;否则为假
你也可以看到 ServiceNow 已经进行了一些脚本编写。这个 if 语句检查表单是否正在加载,或者更改字段的新的值是否为空。如果这两个条件中的任何一个为真,脚本将从函数中返回,实际上取消了脚本。在大多数情况下,这非常有帮助,但了解这个 if 语句是好的,因为有时你可能想要修改它,以便在加载期间或字段变为空值时运行代码。
例如,如果你已经清除了包含附加用户数据的用户字段,你也会想要清除附加的用户数据。在这种情况下,你将想要移除代码返回函数的条件,如果 newValue 参数为空。
当选择 onChange 类型时,开发者将可以看到为脚本运行选择字段的选项。此字段中可用的字段取决于表字段中的值。
我们可以在 图 3.2 中看到一个 onChange 类型客户端脚本:

图 3.2:示例 onChange 客户端脚本
在前面的图中,我们可以看到问题表的 onChange 客户端脚本。特别注意字段名称字段,它默认为 Active,因为这是需要更改以使脚本运行的字段。很容易忘记更改这个字段,当你期望脚本执行时,却不知道为什么它没有执行。
onSubmit
onSubmit 客户端脚本类型在表单保存时运行。这种类型的脚本并不广泛使用,因为我们通常可以使用服务器端脚本来在记录保存后执行操作,但它很有帮助,因为它提供了在运行服务器端脚本之前执行客户端脚本的最后机会。
ServiceNow 为 onSubmit 提供的脚本与 onLoad 非常相似:
function onSubmit() {
//Type appropriate comment here, and begin script below
}
onSubmit 脚本类型可用于在保存之前检查字段值,并在字段中存在无效值时终止保存。
当考虑编写 onSubmit 脚本时,值得思考的是该脚本是否可以在服务器端运行。这是因为客户端脚本在用户面前运行,进一步的加工必须等待脚本完成,而服务器端,代码在用户之外执行,并且可以与其他脚本并行执行。
如果您想终止正在提交的表单,请从 onSubmit 函数返回 false。
我们可以在 图 3.3 中看到一个 onSubmit 示例:

图 3.3:样本 onSubmit 客户端脚本
对于我们的 onSubmit 客户端脚本示例,我们可以看到这个客户端脚本已准备好添加到更改表。
onCellEdit
当在表的列表视图中编辑单元格时,将运行 onCellEdit 类型的客户端脚本。这是唯一不运行在表表单视图中的客户端脚本类型。这种类型的客户端脚本有助于确保您想要坚持的规则也适用于表的列表视图。
ServiceNow 为 onCellEdit 脚本提供的脚本看起来与其他客户端脚本类型略有不同:
function onCellEdit(sysIDs, table, oldValues, newValue, callback) {
var saveAndClose = true;
//Type appropriate comment here, and begin script below
callback(saveAndClose);
}
值得注意的是,这些脚本可能是针对多个记录的,因为您可以使用列表编辑多个记录。如您所见,这种略有不同的脚本类型中有相当多的不同参数。我们将查看每个参数提供了什么:
-
sysIDs: 正在被编辑的记录的所有sys_id唯一 ID 的数组。 -
table: 正在被编辑的记录的当前表。 -
oldValues: 正在被编辑的所有单元格的旧值。 -
newValue: 将放入所有正在编辑的单元格的新值。 -
callback: 一个回调,允许执行进一步的onCellEdit脚本或在没有其他脚本存在的情况下提交所做的更改。可以传递一个true或false参数,分别允许进一步执行脚本和提交更改,或者停止进一步脚本的执行并不提交更改。
这些脚本通常不太常用,因为像业务规则这样的服务器端脚本通常可以用来执行所需的功能。在列表中编辑记录也可以是更新多个记录的强大工具,因此有时会将其锁定为仅对某些用户开放,以防止知识不足的用户造成问题。
一个 onCellEdit 客户端脚本示例可以在 图 3.4 中查看:

图 3.4:样本 onCellEdit 客户端脚本
前图中的客户端脚本是为请求的项目表中的onCellEdit。同样,对于此类脚本,请确保字段名称字段设置为所需的值,因为脚本仅在列表视图中更改此字段时运行。
UI 策略
UI 策略是客户端脚本的更可配置版本。管理员设置一个条件,然后使用操作或脚本修改字段。通过配置,开发者可以更改字段是否为必填、可见和只读。如果您正在考虑对字段进行这些更改,UI 策略通常是最佳选择。UI 策略比客户端脚本更容易理解和维护,对其他管理员来说也是如此。
UI 策略也可以根据设置的条件进行脚本化,例如,如果条件匹配,可以运行代码;如果不匹配,也可以运行代码。在 UI 策略中设置条件与在 ServiceNow 的其他地方设置条件非常相似,因为它使用标准的条件构建器。
UI 策略针对表运行,但由于可以使用条件构建器,因此可以多个字段作为条件构建的一部分使用。UI 策略附带一些复选框,用于指定 UI 策略何时应用。在以下内容中,我们将查看不同的选项以及它们在应用策略时的变化:
-
Global:默认设置,这意味着 UI 策略将默认在所有视图中运行,尽管通过取消勾选全局复选框,您可以选择特定的视图。 -
On load:默认设置,此复选框选中时,在表单加载时以及字段更改时运行策略。 -
Reverse if false:如果条件不成立,则反转 UI 策略的效果。这是默认设置。 -
Inherit:默认未设置,这允许扩展所选表的表继承策略。例如,如果设置了此选项,任务表上的策略将被事件表继承。
UI 策略与其关联的 UI 策略操作相关联,当条件匹配时生效,或者在“反转如果为假”复选框设置的情况下反向生效。这些操作允许字段变为必填、可见、只读或这些效果的组合。UI 策略操作被视为配置,并在字段上采取这些操作时被视为最佳实践。
如果您想通过 UI 策略实现更多功能,有选项可以在匹配或不匹配给定条件时进行脚本化。这很少使用,因为客户端脚本通常更适合此要求,但让我们看看它是如何工作的。
首先,要在 UI 策略中脚本化,您必须设置“运行脚本”复选框。这将出现两个字段:如果为真则执行和如果为假则执行。这些名称相当直观,即如果 UI 策略中定义的条件匹配为真或假,则将运行。
ServiceNow 为我们提供了将代码放入其中的功能,如下所示:
function onCondition() {
}
这个功能在“执行如果为真”或“执行如果为假”脚本字段中是相同的。
UI 策略对于使字段成为必填、可见和只读非常有用。如果考虑在 UI 策略中脚本化,那么配置而不是定制当然被认为是最佳实践,尽管通常最好使用客户端脚本。这是因为你只需要维护一个客户端脚本,而不是 UI 策略上的两个脚本。在客户端脚本中,编写所有可能情况下的代码也更容易,而不是像 UI 策略中那样仅仅匹配或不匹配一组条件。
在图 3.5 中,我们可以看到一个显示脚本表单部分的 UI 策略:

图 3.5:示例 UI 策略显示脚本字段
在前面的图中,我们可以看到一个当前设置为在事件表中运行的 UI 策略。如果我们使用可以看到的两个脚本字段,那么重要的是我们要用这两个脚本覆盖 UI 策略条件为真和假的场景。
如何以及何时编写客户端脚本
当开发者想要更改用户面前的屏幕时,应该编写客户端脚本。这可能是对字段值、可见性或其他更改的更改。每次运行客户端脚本时,都需要从实例中获取资源,因此将客户端脚本保持在最低限度很重要。
如果你可以在服务器端而不是客户端进行更改,那么这样做被认为是最佳实践。这将缩短加载时间并提高用户体验。
让我们看看客户端脚本的一些常见用途以及如何最好地完成这些任务。
使字段成为必填、可见或只读
对于使字段成为必填、可见或只读,最好使用 UI 策略。这种方法使用配置而不是定制,并且更容易维护。
在这种情况下,不需要进行脚本编写,因为你可以使用 UI 策略操作来实现你的目标。ServiceNow 通过条件构建器使这些操作设置变得简单易行。
根据另一个字段的值填充字段
客户端脚本的另一个流行用途是基于另一个字段的值填充字段。这种用法的一个常见例子是在字段中选择用户后,例如,自动填充公司、电话号码和职位名称。
对于这个示例,你将想要使用一个onChange客户端脚本。通过将onChange字段设置为用户字段,你可以使用客户端脚本获取其余的用户数据。对于这一点,最佳实践是使用GlideAjax调用,我们将在下一章的进阶客户端脚本中探讨。
显示/隐藏表单部分
有时候,你可能只想向不同类型的用户显示特定的表单部分。这可以基于许多变量,但通常它将基于用户拥有的角色或他们所属的组。
对于这个需求,可以使用 onLoad 客户端脚本。这将确保在表单对用户可用之前,所需的表单部分可以被隐藏。
复习
我们已经探讨了客户端脚本的一些常见用途,但这绝对不是详尽的列表。在考虑在客户端编写脚本时,通常值得思考它们是否必要,以及它们是否可以更好地放在服务器端脚本中。记住,客户端脚本会增加加载时间,因此尽量将它们保持在最低限度。
测试客户端脚本
在 ServiceNow 中测试脚本可以通过几种方式完成。幸运的是,客户端脚本可能是最容易测试的,因为脚本是在用户面前运行的,因此可以直接向用户展示脚本的日志和反馈。
弹出提示
调试客户端脚本最简单的方法之一是使用 alert 功能。alert 简单地弹出一个消息窗口,显示你选择包含的信息。这可以用于显示在特定时间字段或变量的值,或者简单地确认某些函数已被输入。
让我们看看以下代码中 alert 的作用:
alert('Debug Message');
前面的脚本将简单地向用户显示一个弹出消息,内容为“调试信息”。这是一行简单的代码,可以提供即时反馈。
让我们看看如何使用 alert 来帮助我们测试代码:
function onLoad() {
alert('Start of script');
var shortDescBefore = g_form.getValue('short_description');
alert('The short description beforehand is ' + shortDescBefore);
g_form.setValue('short_description', 'Alerting Issue');
var shortDescAfter = g_form.getValue('short_description');
alert('The short description afterwards is ' + shortDescAfter);
}
在前面的例子中,我们使用三条消息来测试脚本是否按预期工作。第一条消息会简单地通过显示“脚本开始”来提醒用户脚本已经开始。这有助于检查 JavaScript 是否在浏览器上正确运行,或者是否有其他客户端脚本正在引发错误,阻止此脚本运行。
第二条消息给出了表单加载时的简短描述值。我们将简短描述的值存储在 shortDescBefore 变量中,并在我们的 alert 中使用它。我们可以通过在它们之间使用加号将文本字符串和变量组合在一起,在 alert 中。
第三条消息显示了在用 g_form setValue 方法修改后,简短描述字段的值。第三条消息将读作“之后的简短描述是弹出问题”。
使用 alert 允许我们测试和调试快速且有效地编写的脚本。使用 alert 的问题在于,你必须确保在用于测试或调试时移除或注释掉所有 alert,因为这些消息如果不移除,对用户来说非常明显,有些人甚至觉得它们很烦人,需要不断点击。
我倾向于喜欢以这种方式进行初始开发测试,因为它使用起来非常方便。然而,alert 并不提供持续的测试方法,因此对于代码的维护来说,使用更干净的日志方法可能更好。
Jslog
另一种测试和调试客户端脚本的方法是使用jslog。当使用jslog时,您可以在脚本中写入行以将日志发送到 JavaScript 日志。JavaScript 日志可以在开发系统设置中打开,以便查看消息。这种方法只会向已打开 JavaScript 日志的用户显示日志,因此它可以是一种不太侵入性的测试脚本的方法。
jslog消息的内容可以与使用alert的消息相同。让我们看看我们如何在脚本中编写它:
jslog('Testing Message');
在前面的示例中,消息Testing Message将出现在 JavaScript 日志中。Jslog 可以包含字符串或变量,但它不会显示在 ServiceNow 日志中。它只出现在 JavaScript 日志中,因此如果您使用此测试方法,请确保您已打开它。
由于 JavaScript 日志对于普通用户来说通常不会打开,因此可以将这些日志保留以供脚本未来的维护。如果您选择这样做,请确保您留下的日志有良好的文档记录并且尽可能少。对于新手开发者来说,维护一个他们不熟悉的实例,其中 JavaScript 日志不断更新,是非常困难的。
浏览器
浏览器方法不是 ServiceNow 特有的客户端脚本调试方式,但它可能非常有用。因为这些脚本在客户端运行,所以网络浏览器通常有显示正在运行的 JavaScript 代码的方式,您可以进行检查。
这些方法根据您查看 ServiceNow 所使用的浏览器而有所不同,但大多数现代浏览器都将有一些检查正在运行的 JavaScript 的方法,这可以用来测试您的客户端脚本。
我个人倾向于使用 Google Chrome 进行基于浏览器的测试,但其他浏览器也可以同样出色。
脚本示例
介绍了客户端脚本的基本元素以及如何和在哪里使用它之后,我们可以看看一些脚本的例子来进一步加深理解。
我们将从查看一些客户端脚本示例开始。
在这个第一个例子中,我们将使用一个onLoad客户端脚本根据登录用户的角色显示和隐藏表单部分。如果登录用户具有itil_admin角色,我们只会在事件表单上显示相关的记录表单部分:
function onLoad() {
if (g_user.hasRole('itil_admin')) {
g_form.setSectionDisplay('related_records', false);
}
}
如您在示例中看到的,我们正在使用g_user的hasRole方法来确定登录用户是否具有所需的角色。如果没有,我们则使用g_form的setSectionDisplay方法来隐藏表单部分。将此客户端脚本作为onLoad类型放置,可以确保此表单部分立即从用户那里隐藏。
我经常使用这种类型的脚本来隐藏出现在表单部分上的敏感数据。这可以是从财务细节到由 HR 持有的个人信息。
我们可以在图 3.6中看到这个脚本的外观:

图 3.6:用于隐藏表单部分的 OnLoad 客户端脚本
现在让我们看看一个onChange客户端脚本的示例。有时,我们可能希望提高对某些类别事件的处理速度。假设在这个示例中,我们希望将所有网络事件的紧急程度设置为高,数据库事件的紧急程度设置为中等,其他用户的紧急程度设置为低。
我们需要确保为我们的onChange脚本设置字段名称字段的值为Category。让我们看看我们需要查看的代码:
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return;
}
switch(newValue) {
case 'network':
g_form.setValue('urgency', 1);
break;
case 'database':
g_form.setValue('urgency', 2);
break;
default:
g_form.setValue('urgency', 3);
}
}
在这个示例中,我们使用switch case语句根据字段的新值执行不同的操作。这种 JavaScript 技术比使用多个if语句更有效。我们还使用了 ServiceNow 提供的newValue参数,以便快速决定将紧急程度设置为何值。
在本例中设置紧急程度的值时,如果用户认为紧急程度不适合这个特定事件,他们仍然可以修改紧急程度字段。通过将字段设置为只读或设置安全规则,可以锁定由脚本填充的字段。
让我们看看这个客户端脚本在图 3.7中的样子:

图 3.7:基于类别的 OnChange 客户端脚本设置紧急程度
接下来,我们将查看一个使用onSubmit类型客户端脚本的示例。这些脚本不如其他类型的客户端脚本常用,但如果需要,了解如何创建一个是有好处的。
在本例中,我们将检查具有itil角色的用户是否填写了描述。有时你可能希望确保具有更多技术经验的用户在表单中填写更多信息:
function onSubmit() {
if (g_user.hasRole('itil') && g_form.getValue('description') == '') {
g_form.addErrorMessage('Enter a description to save.');
return false;
}
}
在前面的示例中,我们使用g_form.addErrorMessage方法在表单顶部显示消息,如果具有itil角色的用户尝试保存没有描述的表单。这条消息将让用户知道为什么他们无法保存表单。阻止记录保存的代码是从onSubmit函数返回 false。如果在onSubmit脚本的任何点上函数返回 false,则记录将不会保存。
能够阻止表单保存是onSubmit类型客户端脚本的一个重要方面,可以说是其主要用途。
我们编写的示例可以在图 3.8中看到:

图 3.8:OnSubmit 客户端脚本强制 itil 用户更新描述
现在让我们看看一个使用onCellEdit类型客户端脚本的示例。通常,这类脚本用于验证用户在列表视图中单元格中输入的内容。
对于这个示例,我们将确保用户不会在单元格中输入高影响值,以迫使他们打开表单视图来完成此操作:
function onCellEdit(sysIDs, table, oldValues, newValue, callback) {
var saveAndClose = true;
if (newValue == 1) {
alert('High Impact cannot be set on a list view.');
saveAndClose = false;
}
callback(saveAndClose);
}
这个例子使用newValue参数来检查影响被设置到的值。如果它将字段值设置为high或1,这是我们需要在脚本中而不是标签中使用的,那么用户将通过消息被提醒。通过将saveAndClose变量设置为false,我们还可以阻止字段被更新。这是因为回调正在使用这个变量,因此也将callback设置为false。
通常,业务规则可以用来完成与onCellEdit脚本类似的任务,但了解它们是如何创建的很有用。
我们还可以在图 3.9中看到这个脚本在 ServiceNow 中的样子:

图 3.9:OnCellEdit 客户端脚本将列表视图上的影响设置为高
这些示例让我们对不同的客户端脚本类型及其用途有了很好的理解。现在我们将探讨 UI 策略。
当配置 UI 策略时,我们主要只需要使用脚本,如果我们想要运行我们在本章前面描述的“如果为真”和“如果为假”字段中的脚本。
对于这个例子,我们将当 UI 策略条件匹配时更改简短描述,如果条件不匹配则再次清除值。首先,让我们看看“如果为真”脚本:
function onCondition() {
g_form.setValue('short_description', 'Matched condition');
}
由于条件处理了脚本何时运行,我们只需在“如果为真”脚本中执行我们想要采取的操作。这个脚本将更改简短描述字段中的值。
现在让我们看看“如果为假”脚本:
function onCondition() {
g_form.setValue('short_description', '');
}
第二个脚本将在条件不匹配时清除简短描述字段。在这个例子中,简短描述字段将只包含无值或Matched condition文本。在 UI 策略中编写脚本时,要仔细考虑脚本的所有可能结果。在这两个独立的脚本中编写脚本可能更困难,这就是为什么脚本通常在客户端脚本中而不是在 UI 策略中编写。
我们可以在图 3.10中看到包含我们创建的脚本的 UI 策略:

图 3.10:UI 策略设置或清除简短描述
摘要
在本章中,我们探讨了客户端脚本在客户端脚本和 UI 策略中的基本用法。我们研究了四种不同类型的客户端脚本,并了解了每种脚本类型的工作方式。我们还发现了何时以及如何编写客户端脚本,以及如何测试和调试它们。最后,我们查看了一些客户端脚本的实用示例以及不同类型客户端脚本的使用场景。
在下一章中,我们将继续探讨客户端脚本,关注更高级的技术和客户端脚本可以使用的领域。我们将研究脚本操作、用于调用服务器端脚本的 AJAX 调用,以及使用 UI 操作运行客户端脚本。
第四章:高级客户端脚本编程
在本章中,我们将探索客户端脚本的高级方面。在这里,我们将更深入地研究客户端脚本以及使用一些更高级的技术。
本章我们将涵盖的主题包括:
-
UI 动作
-
AJAX 调用
-
高级客户端脚本示例
UI 动作
UI 动作通常被认为是服务器端脚本,但它们也可以作为客户端脚本运行。我们将在下一章中介绍 UI 动作及其在服务器端基础中的基本用法。然而,在这里,我们将探讨在客户端运行 UI 动作的更高级技术。
客户端 UI 动作
要将 UI 动作更改为运行客户端脚本,我们首先需要检查客户端字段复选框。这将带来一些额外的字段,包括选择 UI 动作将与之兼容的列表版本的复选框,但我们最感兴趣的新的主要字段被称为 onClick。
当选择 UI 动作时,onClick 字段会运行其中包含的客户端代码。从视觉上看,这只是一个很小的字段,并不适合代码,因此大多数开发者会在该字段中调用一个函数,并在主脚本字段中定义该函数。
让我们看看这个用法的例子。对于 onClick 字段中的代码,我们只需要调用以下函数:
onClick();
然后,在脚本字段中,我们可以定义函数并包含我们想要在其中运行的代码:
function onClick() {
// Write script here to run when an UI Action is selected
}
这种调用位于脚本字段中的函数的方法被 ServiceNow UI 动作使用,这些动作是随平台提供的。
我们可以在 图 4.1 中看到这些 UI 动作的外观:

图 4.1:包含客户端脚本的示例 UI 动作
在我们的示例 图 4.1 中,我们可以看到 UI 动作作为一个表单按钮,因为那是被选中的复选框。在 Onclick 字段中对 onClick 函数的调用使我们能够在 onClick 函数内部的脚本字段中编写我们需要的任何代码。
调用服务器端脚本
现在我们已经看到了如何在 UI 动作中使用客户端脚本,我们可以进一步探讨在同一个 UI 动作中使用客户端和服务器端脚本。
这可以通过首先在 UI 动作中调用客户端脚本,然后该脚本在代码中调用 UI 动作,从而运行服务器端脚本来实现。一开始想象这个概念可能有点奇怪,所以让我们看看它是如何工作的。
首先,我们需要查看从客户端代码调用 UI 动作的代码行:
gsftSubmit(null, g_form.getFormElement(), '<ui_action_name>');
上一行代码调用 UI 动作,但这次它将在服务器端运行。当使用这种技术时,你必须确保 UI 动作的“动作名称”字段与脚本中引用的名称相同。gsftSubmit 的第一个参数用于控制,但由于我们不希望使用它,所以我们只传递 null。第二个参数是为了获取表单;在我们的情况下,我们只想获取当前的 HTML 表单。第三个参数是动作名称,因此这需要是我们的 UI 动作动作名称。
接下来,让我们看看脚本的服务器端:
if(typeof window == 'undefined')
serverSideCode();
function serverSideCode() {
//Run the server side UI Action code
}
代码的第一部分是一个 if 语句,用于检查我们是否不再在服务器端运行而是在客户端运行。这段小代码还确保我们不会收到浏览器错误。如果 if 语句评估为真,那么我们就调用一个函数来运行我们的服务器端代码。
我们可以使用前面的示例并在我们的脚本字段中调用 onClick 函数:
function onClick() {
// Write script here to run when an UI Action is selected
gsftSubmit(null, g_form.getFormElement(), 'incident_ui_action');
}
if(typeof window == 'undefined')
serverSideCode();
function serverSideCode() {
//Run the server side code
}
这现在给我们一个同时运行客户端和服务器端代码的 UI 动作。我们将在本章后面进一步探讨一些示例。
这种类型的脚本非常有用,通常在提交之前确保某些字段已填写或满足条件,并在运行服务器端代码之前在客户端使用。
AJAX 调用
AJAX 调用是一种从客户端以高效方式调用服务器端脚本的方法。虽然可以在客户端简单地使用 GlideRecord,但这并不被视为最佳实践。值得记住的是,每次我们从客户端调用服务器时,我们都需要从客户端到服务器再返回客户端的往返,这需要时间,并在用户面前表现为延迟。因此,我们希望减少我们进行的服务器调用数量,并确保任何进行的调用都尽可能高效。
AJAX 调用可以在服务器上执行多个 GlideRecord 查询,这节省了我们多次调用服务器的需要。为了使 AJAX 调用工作,我们需要一些客户端脚本和一些服务器端脚本。这通常是一个客户端脚本和一个脚本包含;我们将在后面的章节中进一步探讨脚本包含。
客户端 AJAX
AJAX 调用的客户端需要设置 AJAX 调用并向服务器端传递相关参数。一旦从服务器收到响应,我们可以使用返回的值或值来决定要做出哪些更改。让我们看看 AJAX 调用的客户端是如何工作的:
var ajaxCall = new GlideAjax('serverAjax');
ajaxCall.addParam('sysparm_name','getUserLocation');
ajaxCall.addParam('sysparm_user_id', g_form.getValue('caller_id'));
ajaxCall.getXML(ajaxResponse);
function ajaxResponse(response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
alert(answer);
}
上一示例涵盖了基于返回结果的 AJAX 调用和返回函数的调用。在第一行,我们创建一个新的 AJAX 调用并给它我们脚本的包含名称。在此基础上,我们可以添加要发送到服务器的参数。第一个我们需要每次都包含,因为 sysparm_name 参数是我们想在服务器端调用的函数的名称。
之后,我们可以向服务器端发送我们喜欢的任何参数,以便在服务器端脚本中使用。在示例中,我们通过 sysparm_user_id 参数发送当前调用者。
最后,我们使用一个 callback 函数,该函数使用 getXML 函数返回结果。这允许 AJAX 调用异步工作,客户端代码可以继续运行。您也可以使用 getXMLWait 作为同步调用,但这将停止客户端运行代码,直到服务器端代码完成,因此这不是最佳实践。
在 callback 函数中,我们将答案变量设置为服务器端代码的返回值。在这个例子中,我们只是向用户显示我们的服务器端脚本的执行结果。
服务器端 AJAX
现在我们已经看到了 AJAX 调用的客户端方面,让我们看看实现这一功能所需的服务器端代码。
如前所述,我们需要调用与我们的 AJAX 调用具有相同名称的脚本,并确保它包含一个具有 sysparm_name 参数中名称的函数。我们还需要确保脚本包含客户端可调用的,以便 AJAX 调用可以工作,我们可以通过在脚本包含上检查客户端可调用复选框来完成此操作:
var serverAjax = Class.create();
serverAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getUserLocation: function getUserLocation() {
var userRecord = new GlideRecord('sys_user');
userRecord.get(this.getParameter('sysparm_user_id'));
return userRecord.location.getDisplayValue();
}
});
在这里,我们使用 getUserLocation 函数将调用者的位置返回给客户端。通过使用 GlideRecord 和 get 方法获取用户记录,然后我们可以从该用户记录返回位置给客户端。我们使用显示值在客户端向用户显示;否则,我们简单地显示位置记录 sys_id,这对于最终用户来说意义不大。
通过使用 AJAX 调用,我们可以传递回单个值,如我们的示例所示,或者如果需要,可以传递多个值。数组是传递多个值的好方法,但您也可以使用其他方法,因为响应是一个 XML 文档。
脚本示例
现在我们已经看到了一些更高级的客户端脚本使用方法,让我们看看如何使用这些新学的技术的一些示例。
首先,让我们看看 UI 操作。我们可能想使用 UI 操作来推进变更记录的状态,但我们希望在推进到其他状态之前确保某些字段已填写。我们可以使用客户端和服务器端代码来实现这一点。首先,我们使用客户端代码来验证表单,然后使用服务器端代码对记录进行更改。
我们可以看看这是如何工作的:
function validateForm() {
g_form.setMandatory('justification', true);
//Call the UI Action to run the server side script
gsftSubmit(null, g_form.getFormElement(), 'authorize');
g_form.setMandatory('justification', false);
}
if(typeof window == 'undefined')
setToAuthorize();
function setToAuthorize() {
current.state = -3; //Authorize state
current.update();
}
在示例中,在客户端脚本中,我们正在将我们想要填充的字段在脚本中设置为Mandatory,以确保在表单提交之前填充。这是一种很好的通知用户填写额外字段的方法,因为它使用了 ServiceNow 的Mandatory功能,而不是向用户弹出窗口。你将注意到在提交之后,我们停止字段为Mandatory。这样做是为了如果用户只想简单地保存表单或更改不同的字段值,他们不会被justification字段仍然为Mandatory所阻止。
对于提交表单,我们使用gsftSubmit和我们的 UI 操作的动作名称,在这个例子中是authorize。这允许服务器端脚本运行我们的服务器函数setToAuthorize。由于state字段使用数字作为其值,我们将字段设置为-3,并且,正如示例中所示,添加注释以让其他开发者知道该值代表什么是一种良好的实践;这也可以作为你自己的良好提醒。
我们可以看看这个 UI 操作在图 4.2中会是什么样子:

图 4.2:使用客户端和服务器端脚本授权 UI 操作
使用客户端脚本进行验证和服务器脚本执行操作的方法非常有用,并且对于 UI 操作来说效果非常好。
现在让我们看看一个 AJAX 调用的示例。在这个例子中,我们将查看一个依赖于目录项中变量的目录项。有时用户可能需要撤销某些访问权限,例如当他们离开公司时。当我们选择用户时,我们想确保他们有一些形式的访问权限;否则,在剩余字段中他们将没有选择,这会导致用户体验不佳。
让我们看看我们将放入目录客户端脚本中的客户端脚本:
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) {
return;
}
//Clear the Access field when the user changes to ensure a valid selection is made.
g_form.setValue('variables.access', '');
if (newValue == '') {
return;
}
//Ensure a selected user has some active access else clear field
var ga = new GlideAjax('accessCheck');
ga.addParam('sysparm_name','userHasAccess');
ga.addParam('sysparm_user', newValue);
ga.getXML(AJAXParse);
}
function AJAXParse(response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
if (answer == 'false') {
g_form.addErrorMessage('User has no access to remove.');
g_form.setValue('variables.user', '');
}
}
这将被放入一个目录客户端脚本中作为一个onChange脚本,因此决定了我们的函数名称。当我们使用目录项和变量时,我们必须在变量名称前加上variables,然后使用g_form方法来使用变量名称。在示例中,我们使用了一个user和access变量,并且正如你所看到的,当用户更改时我们清除access变量,这样我们就不会在屏幕上出现数据不匹配的情况,其中用户选择了一些访问权限然后又回填用户。
在我们发起 AJAX 调用之前,我们检查用户字段的新值不是空的。如果是空的,就没有必要往返服务器,所以我们使用return,实际上退出脚本。如果值不为空,那么我们使用 AJAX 调用,调用我们的脚本包含并发送一个包含用户字段中新值的用户参数。
当从服务器返回响应时,如果结果是用户没有访问权限,那么我们可以假设用户将无法选择和删除访问权限。因此,在访问变量中不会有任何选择。与其让用户没有任何选择,我们可以清除用户字段,并让用户知道他们所选的用户没有访问权限来删除。
现在我们已经查看了这个例子的客户端部分,让我们看看服务器端代码。记住,脚本包含名称和函数需要与发起 AJAX 调用的客户端代码相匹配:
var accessCheck = Class.create();
accessCheck.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
/**
* Ajax Call - Returns whether a user has an active access record
*/
userHasAccess: function userHasAccess() {
var uAccess = new GlideRecord('u_access');
uAccess.addQuery('u_user', this.getParameter('sysparm_user'));
uAccess.query();
if (uAccess.next()) {
return true;
}
return false;
}
});
在服务器脚本中,我们使用 GlideRecord 来检查一个自定义访问表,因此使用 sysparm_user 参数,通过过滤结果仅包含该用户的记录。如果我们找到一个访问记录,我们将向客户端返回 true,如果没有找到记录,则返回 false。
通过向客户端脚本返回 false,我们清除表单上的用户变量并显示错误消息,以便登录用户知道他们所选的用户没有访问权限。如果我们假设已经设置了合适的引用限定符,这将阻止登录用户在选择用户后找不到记录时选择访问变量。
由于 AJAX 调用被广泛使用,让我们看看另一个例子。在这个例子中,我们将通知事件表单上的用户,他们所选的更改是否与仍有打开任务的事件相关。
这次,我们将使用客户端脚本发起 AJAX 调用。我们需要它在事件表单上的更改请求字段更改时运行,因此我们将使用 onChange 脚本。让我们看看这个客户端脚本代码将如何看起来:
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (newValue === '') {
return;
}
//Check whether change request has open tasks
var ga = new GlideAjax('changeScripts');
ga.addParam('sysparm_name','changeHasOpenTasks');
ga.addParam('sysparm_change', newValue);
ga.getXML(AJAXCall);
}
function AJAXCall(response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
if (answer == 'true') {
g_form.showFieldMsg('rfc', 'Change has open tasks', 'error');
} else {
g_form.showFieldMsg('rfc', 'Change has no open tasks', 'info');
}
}
在这个例子中,我们再次看到了 AJAX 调用,这次是将所选更改请求作为参数发送。当 change 字段值或 newValue 为空时,我们不需要运行 AJAX 调用,但这次我们确实希望我们的脚本在加载时运行,因此我们从脚本开头移除了 isLoading 检查。
当我们从 AJAX 调用返回时,我们使用 g_form 来显示一个字段消息,该消息出现在字段下方,以显示所选更改请求是否有打开的任务。
现在,我们将查看使这个 AJAX 调用工作的服务器端脚本。将使用脚本包含来保存代码:
var changeScripts = Class.create();
changeScripts.prototype = Object.extendsObject(AbstractAjaxProcessor, {
/**
* Ajax Call - Returns whether a change has open tasks
*/
changeHasOpenTasks: function changeHasOpenTasks() {
var cTask = new GlideRecord('change_task');
cTask.addQuery('change_request', this.getParameter('sysparm_change'));
cTask.addQuery('state', 'NOT IN', '3,4'); //Closed and Cancelled
cTask.query();
if (cTask.next()) {
return true;
}
return false;
}
});
在这个例子的服务器端脚本中,我们使用 GlideRecord 来查找所有仍为传递的更改请求打开的更改任务。在前面的脚本中,我们使用 addQuery 行为 GlideRecord 添加查询,其中状态不是 3 或 4,这相当于关闭和取消。这允许我们将任何其他状态视为打开,即使从开箱即用的设置中添加了更多的活动状态。
这意味着如果找到任何记录,我们可以立即返回 true,因为我们只需要知道至少有一个任务仍然是开放的。如果我们找到一个仍然开放的记录,就没有必要运行其他更改任务,这可以降低处理时间并提高代码效率。
一旦返回值被发送回,相应的字段消息将通过客户端代码显示给用户。这种类型的 AJAX 调用对于向用户提供他们输入数据的额外详细信息非常有用。
AJAX 调用被广泛使用,我肯定会建议尽早熟悉它,以便在 ServiceNow 中进行脚本编写,因为许多需求将需要一个 AJAX 调用来满足。
摘要
在本章中,我们探讨了客户端脚本的进阶方面。我们看到了如何使用 UI 动作来运行客户端代码,并运行客户端和服务器端代码。我们还研究了至关重要的 AJAX 调用,这是一种从客户端高效调用服务器端代码的方法。最后,我们查看了一些如何使用这些高级客户端技术的示例。
在下一章中,我们将转向服务器端,并了解如何开始编写服务器端脚本。这包括业务规则、UI 动作和访问控制。我们还将探讨这些脚本应该在何时编写,如何测试它们,以及一些实用示例来帮助您入门。
第五章:服务器端脚本简介
在本章中,我们现在将探讨脚本的服务器端。在这里,我们通过介绍生成服务器端代码的基本知识来开始编写服务器端脚本。我们还将了解如何测试这些脚本,并查看一些示例。
在本章中,我们将探讨以下主题:
-
商业规则
-
UI 动作
-
访问控制
-
如何以及何时编写服务器端脚本
-
测试服务器端脚本
-
服务器端脚本示例
商业规则
商业规则通常是使用最广泛的服务器端脚本方法。可以使用商业规则进行基本的配置,而不是定制。我可以说,对于大多数需要商业规则的需求,你通常需要添加一些形式的定制,即脚本。
商业规则的一个特别有用的方面是它们可以以不同的方式被触发。这允许我们根据对记录发生的操作,在服务器端脚本的不同时间运行。
当首次查看商业规则表单时,有两个选项来决定何时运行商业规则。这些是在记录的插入或更新时。在这个基本上是基本视图的商业规则中,我们可以配置商业规则在筛选条件或角色的所有者以及更改字段值时运行。如果我们想做的更多,这通常是情况,我们需要在表单上勾选高级复选框。
一旦勾选了高级复选框,我们就有四个选项来决定商业规则应该运行的动作。让我们看看这些选项是什么,以及它们确切运行的时间:
-
插入:在插入新记录时运行
-
更新:在记录更新时运行
-
删除:在记录被删除时运行
-
查询:当搜索此类记录时运行
商业规则运行的时间也取决于“何时”字段的值。在这里,我们也有四个选项可供选择:
-
在保存记录之前:在记录保存之前运行
-
之后:在记录保存后立即运行
-
异步:在调度器运行一个在记录保存后发送到计划作业队列的计划作业时运行
-
显示:在从数据库获取数据后加载表单之前运行
当你正在更新记录本身时,最好使用“之前”商业规则;这样,更改是在插入或更新之前进行的,因此记录只更新一次。应该使用“之后”商业规则,当脚本不影响当前记录时,这样脚本就可以在保存后运行。
异步将在未来的某个时刻运行,所以只有当脚本中的操作不需要立即看到时,我才会使用它。这可以是排队事件以发送通知或从 ServiceNow 发送作业。显示业务规则最好用于在草稿板上保存数据,以便客户端脚本可以利用它。这很有帮助,因为它阻止客户端脚本需要往返服务器,因为显示业务规则已经收集了所需的数据。
结合这些复选框和“当”字段,我们可以为运行我们的服务器端脚本提供良好的触发器选择。最常见的是勾选“插入”和“更新”复选框的“之前”或“之后”的“当”字段值。
当勾选了高级复选框后,我们还可以看到高级表单部分。这为我们提供了运行脚本的条件和用于放入我们的服务器端代码的脚本字段。
现在我们已经了解了业务规则的格式,接下来让我们看看如何添加脚本。我们只需要设置脚本运行时的表单,如果需要的话添加一个条件,然后在脚本字段中添加我们的代码。
作为例子,我们将创建网络类别事件时将其影响设置为高。为此,我们需要在条件字段中添加一个条件:
current.category == 'network'
这个条件字段有助于在不必要时停止运行脚本。在先前的代码中,我们只有在类别是network时才会运行我们的脚本。
现在我们来看看我们将运行的脚本:
(function executeRule(current, previous /*null when async*/) {
current.impact = '1'; //High Impact
})(current, previous);
首先,让我们看看 ServiceNow 为我们提供的业务规则代码。我们提供的executeRule函数有两个参数:current和previous。current是当业务规则被触发时包含记录所有当前值的对象。previous是包含任何更新之前记录值的对象,本质上持有前一个值。Previous 只能与update和delete操作一起使用。
我们的示例将网络类别事件的影响设置为高。对于我们的示例,我们将“当”字段设置为“之前”,这意味着我们不需要添加任何脚本来更新记录,因为我们是在记录被保存之前更改当前记录。我们还需要在我们的业务规则中勾选“插入”复选框。我们可以在图 5.1中看到业务规则本身:

图 5.1:为网络类别事件设置高影响力的业务规则
这为我们概述了业务规则的工作方式,并且在 ServiceNow 平台上被广泛使用。
UI 操作
UI 操作是你可以在 ServiceNow 中找到的按钮、上下文菜单选择、链接和列表选择。因为你可以让 UI 操作出现在平台的不同区域,所以它们可以非常方便地添加你的脚本。
我倾向于发现表单按钮在 UI 动作中最常被使用。这些可以作为有用的附加按钮添加功能或以不同状态移动记录的方法。
UI 动作通常在服务器端运行;然而,我们也可以在客户端运行它们,正如我们在客户端章节中探讨的那样。通过使用复选框,我们可以在创建记录时插入 UI 动作,或者在记录已存在时更新。
首先,让我们看看您可以将 UI 动作显示给用户的不同方式。通过勾选相关的复选框,您创建的 UI 动作可以以一种或多种方式显示。让我们看看这些选项:
-
表单按钮:这将在表单上以按钮的形式显示 UI 动作,类似于更新按钮
-
表单上下文菜单:在上下文菜单中显示 UI 动作,该菜单在您右键单击表单标题栏时出现
-
表单链接:在相关链接部分以链接的形式显示 UI 动作,该部分出现在表单部分和关联列表之间
-
列表横幅按钮:在列表视图的顶部以按钮的形式显示 UI 动作,位于表标签旁边
-
列表底部按钮:在列表底部以按钮的形式显示 UI 动作
-
列表上下文菜单:在记录列表中通过右键单击显示 UI 动作
-
列表选择:UI 动作出现在列表底部选定行的操作选择中
-
列表链接:在列表底部相关链接部分显示 UI 动作
与业务规则类似,我们同样为 UI 动作获取一个条件和脚本字段。然而,脚本字段在 UI 动作上开始时是空的,因此开发者必须提供所有代码。
我们将查看如何编写一个基本的 UI 动作脚本。以我们的示例为例,我们将构建一个表单按钮来将事件状态更改为“进行中”。让我们看看我们需要编写的代码:
//Moves the incident state to In progress
current.state = 2; //In progress
current.update();
action.setRedirectURL(current);
在前面的代码中,我们将状态设置为 2 的值,这对应于事件的状态“进行中”。一旦设置了此值,我们只需要使用 current.update 更新记录。
action.setRedirectURL 这一行在 UI 动作中用于在服务器端脚本运行后重定向到当前记录。如果在表单上按下 UI 动作而没有使用重定向代码,它将返回到上一个屏幕,通常是选择记录的列表视图。
在我们的脚本中,这条重定向行表示代码运行后,将页面重定向到不同的位置;在我们的示例中,我们使用 current 作为当前记录。然后它将重定向回我们所在的记录。如果我们想保持用户在同一个记录上并显示我们的 UI 动作所做的更改,这很有用。在这种情况下,用户应该能够看到状态变化。
我们可以在图 5.2中看到 UI 动作:

图 5.2:将状态字段移动到进行中的 UI 操作
在这里,我们只使用我们的 UI 操作作为表单按钮,但我们可以通过使用右侧的复选框以其他方式显示它。此 UI 操作将在带有“显示插入”和“显示更新”复选框勾选的新和现有事件表单上出现。
访问控制
访问控制是 ServiceNow 的安全方面,访问是否授予主要受配置的角色控制。然而,如果需要更复杂的计算来确定用户是否允许访问,我们需要使用脚本来处理这一点。
通常,访问控制无需脚本即可定义,但仍有许多场景需要使用代码。这些通常是在需要用户信息超出其角色范围时;例如,他们的组成员或公司。
要修改访问控制,您需要拥有 security_admin 角色,并确保在尝试修改之前提升权限并使用它。要访问访问控制的脚本方面,您需要勾选高级复选框。一旦完成,脚本字段将出现在表单底部。
对于每个访问控制,只有当用户匹配访问控制的三个方面:角色、条件和脚本时,才会授予用户访问权限。在编写您的访问脚本时,这值得记住,因为可能是角色或条件阻止了访问,而不是您编写的代码。
现在让我们看看一些访问控制代码。在这里,我们将允许如果用户是当前分配组的成员,则对任务编号字段进行写访问:
gs.getUser().isMemberOf(current.assignment_group);
此示例将允许用户访问,如果他们是当前分配组的成员。访问控制中的脚本需要将答案变量设置为 true 或 false,或者简单地评估为 true 或 false。在示例中,我们只是使用评估方法。
在图 5.3中,我们可以看到整个访问控制,允许对任务编号字段进行写访问。这适用于任务表;例如,事件、变更和问题:

图 5.3:任务表上数字字段的访问控制
访问控制脚本可以相当简短,并且您通常不会在这里遇到复杂的代码,但得到正确的代码非常重要,因为访问控制可以成为 ServiceNow 的一个非常复杂的领域,有许多规则适用于或不适用于授予或拒绝用户访问。
如何以及何时编写服务器端脚本
服务器端脚本通常比客户端脚本更受欢迎,因此应在可能的情况下使用。由于服务器端脚本通常在用户前端之外运行,因此它不太可能影响用户会看到的不良用户体验的加载时间。
虽然如此,仍然建议在自定义之前进行配置,并在可能的情况下避免脚本化。这是因为脚本更难维护,并且更有可能在 ServiceNow 的版本之间引起问题。
让我们探讨一些服务器端脚本的一些常见用途以及如何最佳完成这些任务。
更改表单值
通过业务规则或 UI 操作来更改表单值通常是最佳选择。如果你想在使用者点击时更改值,那么 UI 操作是最好的;然而,如果你只想在满足某些条件时始终更改值,那么业务规则会更好。最好使用前置业务规则,这样表单就只会更新一次。
限制表单和字段的访问
在限制表单和字段的访问时,最好使用访问控制。访问控制将限制无论以何种方式访问表单的访问,因此它是一个强大的工具。如果你只需要使用角色或条件来控制表单和字段的访问,那么这是首选方法,因为这被视为配置,并且更容易维护。
如果你的访问要求比这更复杂或更复杂,那么你需要向访问控制中添加脚本。最常见的原因是用户需要成为某些组的成员,或者用户在表单的字段中被引用。
将值传递到客户端
有时候,你可能需要将值传递到客户端,因为它们在加载的表单中不可见,而这些值可能对运行客户端脚本很有用。在这种情况下,你将想要使用显示业务规则。这允许在显示业务规则中设置临时值,然后可以在表单的客户端脚本中使用这些值。
如果你考虑在onLoad客户端脚本中调用服务器,那么通常更好的做法是使用显示业务规则,因为这样可以消除对服务器额外调用的需求。
我们可以在图 5.4中看到显示业务规则的外观:

图 5.4:显示业务规则示例
值得注意的是,一旦我们选择了“何时运行”字段作为显示,我们就不再有插入、更新、删除或查询复选框可供选择。这是因为显示业务规则总是在表单加载之前以相同的时间运行。
复习
我们已经查看了一些在服务器上脚本化以及在不同场景下如何最佳实现这些任务的例子。服务器端脚本通常比客户端脚本更常见,并且占大多数 ServiceNow 实例代码的大部分。
因此,我们实际上刚刚开始探讨服务器端脚本的可能性。代码中存在巨大的潜力,我们将在下一章中探讨一些更高级的方法。
测试服务器端脚本
与客户端脚本一样,测试服务器端脚本有几种方法。这些方法通常不如客户端调试技术那么直接,但在修复代码时是必不可少的。
gs.log
我们将要查看的第一种日志技术是gs.log。这是一种非常流行的技术,并且仍然被开发者广泛使用。正如我们在第二章探索 ServiceNow Glide 类中看到的,gs.log让我们能够将日志发送到系统日志,带有脚本源,这样我们可以在代码执行时发送消息。
我们可以从任何服务器端脚本中创建gs.log,这使得它非常有用;然而,我们无法在范围应用程序中使用它。我们将在稍后查看范围应用程序的日志记录。只要你在全局范围内,你就可以使用gs.log并向系统日志发送消息。
让我们回顾一下使用gs.log的简单代码片段:
gs.log('Server Side Log Message');
当脚本运行时,此日志将出现在应用程序导航器中的脚本日志语句模块中。我们还可以使用此方法在脚本中的某个阶段显示变量的值。我们可以使用此方法来显示事件记录上的调用者:
var user = current.caller_id;
gs.log('The current caller is ' + user);
如果在业务规则中使用,这将显示日志中的当前事件调用者。我们可以使用加号符号向我们的日志添加字符串和变量。
此方法允许我们在脚本中添加尽可能多的日志,以检查方法是否在代码的某些点上被调用或变量的值。
记得在将代码上线之前删除所有的gs.log语句或将其注释掉。代码中留下太多的日志行会使系统日志难以调试。
范围应用程序的日志记录
正如我们在查看gs.log时发现的,它不在范围应用程序中工作。在范围应用程序中进行日志记录时,我们需要使用不同的方法。实际上,有四种方法可以使用。
范围应用程序中的四个日志级别是:
-
错误 -
警告 -
信息 -
调试
这种类型的日志记录与gs.log非常相似,但用不同的日志级别替换了gs之后的单词log。
让我们看看如何为每个编写一个基本的脚本:
gs.error('Error log');
gs.warn('Warn log');
gs.info('Info log');
gs.debug('Debug log');
这些日志可以在应用程序日志表中查看,可以通过访问系统日志 | 系统日志 | 应用程序日志来实现。
默认情况下,当你创建一个新的应用程序范围时,只有错误、警告和 info 消息会被显示,这是由与范围相关的系统属性设置的。这个属性将被命名为<scope name>.logging.verbosity,可以在四个级别中的任何一个设置。对于每个设置的级别,它将显示该级别和任何更高级别的消息类型。由于属性的默认值是 info,我们将看到所有消息,除了 debug。
通常,许多开发者会简单地使用信息消息作为在服务器端调试脚本的通用方式。然而,如果你正在将调试功能集成到创建的范围内应用程序中,最好使用所有级别类型以获得更完整的解决方案。
会话调试
ServiceNow 还提供了一些服务器端调试辅助功能,形式为会话调试。这些主要列在系统诊断应用程序中的模块中,但在系统安全应用程序中也有安全调试可用。
通过点击这些模块,你可以激活特定区域的调试;例如,业务规则。这会持续到会话结束,或者你通过使用禁用所有模块来关闭调试。
我发现这些模块很有用,主要是用于安全规则调试,但开发者通常并不全面地使用它们。
脚本调试器
脚本调试器是在伊斯坦布尔版本中引入的,用于服务器端脚本。这允许开发者设置任何服务器端脚本的断点,然后在调试器中逐步执行。
要使用调试器,你必须首先在脚本中点击左侧边缘以创建断点。然后,要加载脚本调试器,导航到系统诊断 | 脚本调试器。脚本调试器在一个新窗口中显示,你可以看到在脚本中创建的断点,如图 5.5 所示:

图 5.5:脚本调试器逐步执行事件业务规则
一旦打开调试器,触发脚本,你将获得启动调试的选项,这将允许你逐步执行脚本。
如果你需要找出脚本中的问题所在,但不确定问题出在脚本中的哪个位置,这是一个有用的工具。这也是一个很好的工具,用于处理通常包含在脚本包含中的较长的脚本。
脚本示例
现在我们已经了解了服务器端脚本的基本工作原理,我们可以看看一些脚本示例,以进一步了解如何使用这些入门级服务器端技术。
我们将从一个更进一步的业务规则示例开始。
这次,我们将创建一个删除前业务规则,以确保董事不会被删除。我们需要将业务规则设置为高级;选择删除复选框并在当字段中选择“之前”。让我们看看代码:
(function executeRule(current, previous /*null when async*/) {
if (current.title == 'Director') {
gs.addErrorMessage('Cannot delete Director');
current.setAbortAction(true);
action.setRedirectURL(current);
}
})(current, previous);
在这里,我们正在使用标题字段检查用户是否是董事,如果是,则使用 setAbortAction 来阻止 delete 操作进行。为了确保用户得到通知,我们还使用 addErrorMessage 向用户显示消息,并使用 action.setRedirectURL 保持用户在当前记录上,这样他们就可以看到 delete 没有进行。
该规则可以在 图 5.6 中看到:

图 5.6:停止删除董事的业务规则
接下来,我们将查看一个 UI 操作示例。对于这个 UI 操作,我们将创建一个按钮,将事件分配给自己。首先,我们将添加一个条件,只有当它是分配组的当前成员时,按钮才会在表单上显示:
gs.getUser().isMemberOf(current.assignment_group)
这行代码确保登录用户是分配组的成员,以便可以将事件分配给他们。将事件分配给用户的代码如下所示:
//Assign to the current logged in user
current.assigned_to = gs.getUserID();
current.update();
这两行将事件的分配给值设置为当前登录用户,然后更新记录以保存更改。这是一个相当简单的 UI 操作,但非常有帮助。
在这里,在图 5.7中,我们看到我们创建的 UI 操作:

图 5.7:将事件分配给登录用户的 UI 操作
通常,与 UI 操作相关的脚本可能不需要很长就能成为有效的解决方案并为用户提供价值。
最后,我们将查看一个访问控制脚本示例。有时可能有必要限制对单个组的访问,甚至排除系统管理员查看记录。
我们将使用变更请求表来演示此示例,但显然,存在其他需要停用的读取访问控制,以便我们的新访问控制能够工作。我们只允许eCAB Approval组访问软件类别的变更。
让我们看看代码:
answer = false;
if (current.category == 'Software') {
if (gs.getUser().isMemberOf('eCAB Approval')) {
answer = 'true';
}
} else {
answer = 'true';
}
在这里,如果类别不是软件,我们允许访问;或者,如果它是软件,用户必须是eCAB Approval组的成员。我们将清除管理员覆盖复选框,因此即使是管理员也需要遵守规则才能获得访问权限。我们还向规则添加了itil角色,以确保此角色对于授予访问权限是必需的。
我们可以在图 5.8中看到我们创建的规则:

图 5.8:将访问限制为 eCAB Approval 组成员的软件类别访问控制
这种类型的访问控制对于敏感信息可能很有用,也许甚至系统管理员也无法查看。
摘要
在本章中,我们通过业务规则、UI 操作和访问控制来查看服务器端脚本的基础。我们看到了业务规则的多种不同运行时以及 UI 操作显示给用户的多种方式。我们探讨了何时适合编写服务器端脚本,以及一些常见用途的示例,以及如何测试服务器端脚本。我们还查看了一些使用所学技术的服务器端脚本的实际示例。
在下一章中,我们将探讨服务器端脚本的进阶方面,包括脚本包含、计划任务和后台脚本。我们还将查看如何在工作流中编写脚本以及脚本操作,以及如何设置事件。我们将介绍这些服务器端高级主题,并展示一些实际示例。
第六章:高级服务器端脚本编程
在本章中,我们将探讨服务器端脚本的高级方面。我们将探讨一些更高级的方法和技术,这些方法和技术可以在服务器端使用,建立在上一章所学内容的基础上。
在本章中,我们将探讨以下主题:
-
脚本包含
-
定时任务
-
背景脚本
-
工作流程脚本
-
事件管理
-
脚本操作
-
高级服务器脚本示例
脚本包含
脚本包含是 ServiceNow 脚本的核心,并且在编写代码时可以说是最常用的。脚本包含用于存放代码类,以及 ServiceNow 平台使用的许多后端脚本。
在创建您的脚本包含时,您首先需要给它一个名称。这个名称将非常重要,因为它将在其他代码中用于调用脚本包含中的方法。
确保您为脚本包含选择的名称中不包含空格。最好使用下划线在名称中分隔单词。
一旦为您的脚本包含输入一个名称,您会注意到 API 名称和脚本字段已被填充。API 名称是只读的,根据创建此脚本包含的应用程序的名称以及脚本包含本身的名称给出。
脚本字段填充了一些用于创建此脚本包含类的简介脚本,考虑到脚本包含的名称。例如,如果我们命名我们的脚本包含为script_utils,我们会得到以下脚本:
var script_utils = Class.create();
script_utils.prototype = {
initialize: function() {
},
type: 'script_utils'
};
这为我们提供了创建一个可以从服务器端调用的脚本包含类的基代码。在创建脚本包含时,首先要问的问题之一是脚本是否仅从服务器端调用,还是从客户端调用。这是因为如果我们打算从客户端调用脚本包含(例如,用于 AJAX 调用),我们需要勾选客户端可调用复选框,并且在这个过程中,你也会注意到代码也发生了变化。
对于我们的示例,这将更改代码如下:
var script_utils = Class.create();
script_utils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
type: 'script_utils'
});
这个更改后的脚本允许脚本包含继承AbstractAjaxProcessor。如果没有这个,AJAX 调用将不会工作,所以如果脚本包含将在客户端调用,这一点非常重要。
虽然这是 ServiceNow 为您提供的入门代码,但并不需要在脚本包含中创建一个类。您也可以简单地创建一个在调用服务器端脚本时在脚本包含中调用的函数。
让我们看看一个简短的例子,看看它是如何做到这一点的:
function script_include_test() {
return 'Test Complete';
}
这是一个简单的函数,它只会返回文本字符串,但可以简单地作为一个独立的函数调用,而不是创建整个类。
现在我们来看如何创建一个包含类和方法的脚本包含。我们可以向我们的脚本包含中添加任意多的方法:
var script_utils = Class.create();
script_utils.prototype = {
initialize: function() {
},
testMethod: function testMethod() {
return 'Method completed successfully';
},
type: 'script_utils'
};
当在服务器端调用我们的testMethod方法时,它将返回一个字符串。要调用它,我们使用以下代码行:
new script_utils().testMethod();
我们还可以将此代码行分配给变量,或者如果方法返回真或假,则用于条件检查。
脚本包含也可以相互调用,以及它们包含的方法。在构建服务器端脚本的批量处理时,通常最好考虑脚本包含的作业。查看 ServiceNow 平台附带现有的脚本包含是提高编写脚本包含知识的好方法。
计划中的作业
计划中的作业是一种生成报告或记录并自动运行脚本的方式。创建报告或记录是一种配置,不需要脚本编写。对于我们来说,计划中的作业最感兴趣的是能够自动运行脚本的能力。
计划中的作业是一种在您选择的时间运行脚本的有用方式。这可以是一夜之间的作业,也可以是每小时运行一次的脚本。根据我的经验,在非工作时间运行脚本是计划中的作业的主要好处和用途之一。常见的用途包括在早上最早为用户准备数据,或者删除旧数据。
当创建计划中的作业时,我们通过使用运行字段来设置计划中的作业的频率。该字段中的不同时间选项为开发者提供了不同的字段来填写,以便适当地安排脚本执行。如果选择按需选项,则只有在按下立即执行按钮时脚本才会运行。
除了日程安排,我们还可以为计划中的作业添加一个条件,这样它只有在条件评估为真时才会运行。如果你勾选了条件复选框,则会显示一个额外的条件字段。对于这个条件字段中的脚本,代码的最终表达式需要评估为真或假。
让我们看看一个计划中的作业的条件脚本的示例:
var dateTime = new GlideDateTime();
dateTime.getDayOfWeek() != '7'
在示例中,我们正在检查当前日期是否为星期日,如果是,则不会运行计划中的作业脚本。getDayOfWeek方法给我们提供了星期几,其中一代表星期一,七代表星期日。
上面的脚本第二行可能会出现语法错误,表示缺少分号,并且在函数调用或赋值时看到了表达式。通常这将是有效的,但在计划中的作业的条件字段中,我们期望一个表达式,在这里添加分号会导致脚本中断。
现在,让我们看看“运行此脚本”字段。这是我们将希望在定义的计划时间运行的代码放入的字段。
例如,让我们记录我们的代码已经运行,并且它不是星期日:
gs.log('Today is not Sunday');
我们可以在该字段中编写任何我们喜欢的服务器端脚本,以在指定的时间运行。我们将在本章后面更详细地查看一个更高级的示例。
如果我们将计划中的作业设置为每天凌晨 1:00 运行,我们可以看到它在图 6.1中的样子:

图 6.1:检查是否为周日的计划任务
计划任务是一种在非工作时间运行脚本执行服务器端任务的好方法。
背景脚本
背景脚本是在 ServiceNow 平台上管理员可以立即运行的服务器端脚本。背景脚本无法保存,也不以记录的形式存在。然而,它们对于尝试脚本和修复实例的单一问题非常有用。
要开始创建背景脚本,你只需在系统定义应用程序中选择“脚本 - 背景模块”。这会弹出一个大框供开发者编写代码。如果你有管理员权限但看不到该模块,可以设置一个系统属性,这意味着需要提升权限才能访问背景脚本。如果情况如此,这通常意味着你需要安全管理员角色才能访问背景脚本。
因为背景脚本赋予了在平台上运行任何 JavaScript 的能力,所以出于安全目的,这个模块通常会被更安全地锁定。
在大脚本框下方有几个选项和按钮出现,用于背景脚本。这些是:
-
运行脚本(按钮):在服务器端运行当前在运行脚本框中的脚本
-
范围内(下拉菜单):如果需要,允许在全局之外的不同范围内运行背景脚本
-
在沙盒中执行?(复选框):以沙盒限制执行脚本;例如,无法插入、更新或删除数据
-
4 小时后取消(复选框):如果脚本在四小时后仍在运行,则取消脚本
让我们看看一个背景脚本的例子:
var inci = new GlideRecord('incident');
inci.addQuery('category', 'software');
inci.query();
while (inci.next()) {
gs.log('Incident ' + inci.number + ' would be deleted');
//inci.deleteRecord();
}
在这个例子中,我们正在检查所有具有软件类别的所有事件。在背景脚本中,我们正在记录我们找到的每个事件,以便在执行删除之前审查列表。一旦我们对将要删除的列表感到满意,我们将在日志中看到可以删除日志和删除注释行,从而允许删除操作进行。
这种类型的例子在执行大量记录删除操作时非常有用,可以在执行脚本之前评估影响。
让我们看看背景脚本的样子:

图 6.2:用于记录删除记录的背景脚本
背景脚本对于在执行操作前运行脚本以检查结果以及测试长时间过程中涉及的脚本部分非常有帮助,这样就不需要每次都遍历整个过程来测试代码。
工作流脚本
工作流使用多个活动在 ServiceNow 中构建流程。这些活动通常可以在基本级别上使用,无需脚本。然而,为了构建更高级的工作流,我们可以使用代码来增强我们构建的工作流。
脚本可以出现在许多工作流程活动中,包括审批和任务创建。对于审批,可以使用脚本将用户和组添加到审批活动中。在任务创建中,我们可以在任务创建之前使用脚本设置任务上的值。
然而,在工作流程中脚本的主要编写区域是在运行脚本工作流程活动中。这个活动允许开发者在工作流程中的某个点上运行他们喜欢的任何服务器端脚本。这可以用来操作记录、启动集成或使用脚本执行其他结果。
在工作流程中,你经常遇到脚本使用的另一个领域是设置审批。一个简单的审批工作流程活动将简单地选择一个用户或组进行审批,或者可能是某个特定字段的值。然而,如果你需要选择相关的审批比这更复杂,那么你可能需要使用一些代码。
使用脚本,你可以添加额外的元素来决定谁将被选中进行审批,例如,通过检查审批用户的属性或被审批的记录。可能的情况是,如果一个记录有特定的类别,它将被发送到特定的审批组,或者记录的审批者必须具有特定的角色。这种功能可以通过脚本实现,并且可以适应许多其他场景。
构建复杂审批系统的潜力是巨大的,但请务必确保你创建的内容具有价值并且可以维护。
让我们看看一个运行脚本工作流程活动中的工作流程脚本示例:
//Adds urgent to the short description if the priority is critical
if (current.priority < 2) {
current.short_description = current.short_description + ' URGENT';
current.update();
}
在示例中,如果优先级是关键,我们将URGENT添加到记录的简短描述中。我们还可以在流程脚本中访问当前记录,当前记录是工作流程启动的那个记录。
让我们看看这个活动在图 6.3中会是什么样子:

图 6.3:基于优先级更新简短描述的工作流程活动
工作流程脚本可以特别方便地为你的工作流程添加额外的功能层。有时,当你查看可用的活动时,你可能找不到一个能满足你要求的活动,在这种情况下,脚本可以是解决方案。
事件管理
在 ServiceNow 中,实例运行中触发不同结果的事件。事件可以从服务器上的任何脚本中调用。事件被触发的主要结果要么是通知,要么是脚本操作。通知通常是电子邮件,我们将在本章后面讨论脚本操作。
首先,要触发一个事件,需要定义该事件。要定义一个事件,我们可以导航到系统策略 | 事件 | 注册表,然后点击新建按钮。执行此操作后,我们将看到图 6.4中所示的形式:

图 6.4:新事件表单
在表单中,我们需要为事件提供一个名称。事件名称通常是点号和下划线分隔的单词,如果您查看事件列表,您将看到用于事件的通用格式。填写表格字段作为参考也是良好的实践。触发者和描述字段是简单的文本字段,但填写这些字段以详细说明事件应该在何时触发以及如何触发事件是个好主意。然后,其他人可以使用事件,或者另一个管理员可以轻松地找到其触发点。
一旦我们定义了一个事件,我们就需要能够调用该事件以将其添加到事件队列。我们可以使用GlideSystem方法eventQueue,如下面的代码所示:
gs.eventQueue("custom.event", current, gs.getUserID(), gs.getUserName());
在前面的代码中,我们将调用事件custom.event,这需要替换为您创建的事件的名称。第二个、第三个和第四个参数分别是一个对象和两个字符串。第二个参数几乎总是当前记录,这样一旦事件处理完毕,就可以使用当前记录的数据。第三个和第四个参数更常见地被称为 Parm1 和 Parm2。这些可以在脚本或通知中使用;例如,您可以将电子邮件发送到 Parm1 中持有的值。
在这个示例中,我们正在发送当前登录用户的 ID 和名称。这些参数中发送的信息通常会变化,并且通常与当前用户或事件处理完毕后有用的值有关。
我们还可以使用GlideSystem方法eventQueueScheduled,它与前面提到的非常相似,只是第五个参数是事件应该运行的时间。这可以设置为glide_date_time类型的字段值。
我们可以在以下代码中看到这个示例,使用之前提到的相同事件:
gs.eventQueueScheduled("custom.event", current, gs.getUserID(), gs.getUserName(), current.dateTimeField);
一旦事件被触发,它将很快被处理,除非它被安排在特定的时间,并且已经处理的事件可以在事件日志中看到。要查看事件日志,请导航到系统日志 | 事件。从这里我们可以看到处理的事件、处理时间和尚未处理的事件。
事件提供了一种处理多个脚本或基于单个事件发送通知的绝佳方式,并且由于事件可以在任何服务器端脚本中触发,因此触发事件的方式几乎是无限的。
脚本操作
脚本操作通常是服务器端脚本中较少使用的方法之一。它们在系统调度器中某个事件运行之后执行。这可以作为在未来的某个设定点运行脚本的一种有用方式。
在 ServiceNow 中已经存在一些系统事件,您可以使用这些事件来运行脚本操作,或者您可以创建自己的事件,并从任何服务器端脚本中执行它们。
当创建脚本操作时,你需要选择运行脚本操作的触发事件。这是从列表中选择的,所以在创建脚本操作之前,请确保事件存在。还有一个选项可以添加条件脚本,其编写风格与业务规则相同。你也可以在主脚本中执行条件检查,但如果可以使用条件脚本,则被认为是更好的选择。
让我们看看一个正在使用的脚本操作。主脚本字段可以运行任何服务器端脚本。在这里,我们将添加一个简单的日志:
gs.log('Approval Inserted');
这个例子将向系统日志发送一个日志,表示已经插入了一个审批。这对于示例或调试来说是可行的,但不建议作为生产实例的脚本,因为会创建许多审批,这会导致日志被此消息淹没。
让我们看看脚本操作本身,如图 6.5所示:

图 6.5:审批-日志脚本操作
脚本操作也可以用来设置将来要执行的操作;例如,将记录保留到未来的某个时间。
脚本示例
我们看到的高级服务器端技术可以用多种方式使用,你会在 ServiceNow 平台上发现高级服务器端脚本是最常见的。
现在我们已经看到了编写高级服务器端代码的位置,我们可以看看一些进一步的例子来巩固我们的理解。
让我们先看看一个脚本包含的例子。我们将编写一个脚本以返回请求项的活动任务。这可以帮助用户知道哪些任务需要完成才能使项得到满足。
首先,我们将查看脚本包含的代码:
var item_utils = Class.create();
item_utils.prototype = {
initialize: function() {
},
getActiveTasks: function getActiveTasks(item) {
var tasks = [];
var task = new GlideRecord('sc_task');
task.addQuery('request_item', item.sys_id);
task.addActiveQuery();
task.query();
while (task.next()) {
tasks.push(task.number.toString());
}
return tasks;
},
type: 'item_utils'
};
我们使用getActiveTasks方法获取所有返回给我们的活动任务,通过传递项目参数作为请求项记录来获取任务。使用gliderecord查询,我们可以找到所有任务,并将它们作为数组返回。
在这个例子中,我们已经开始了项目工具脚本包含的创建。你通常会发现在其他开发者开发的 ServiceNow 实例中存在这些脚本。在这里,我们创建了第一个方法,但通常你会在这个类中为请求项构建更多方法,以保持代码的整洁。
我们可以在图 6.6中看到脚本包含的内容将是什么样子:

图 6.6:项目工具脚本包含
现在我们有了脚本包含,我们需要从另一个位置调用它。在这里,我们将从业务规则中这样做,并简单地记录结果。你也可以使用这种类型的脚本包含来帮助决定是否关闭请求项。
下面是调用脚本包含的业务规则的代码:
(function executeRule(current, previous /*null when async*/) {
var tasks = new item_utils().getActiveTasks(current);
gs.log('Active tasks for ' + current.number + ' are ' + tasks.toString());
})(current, previous);
我们将在请求项表中运行这个业务规则。在代码块中,我们将脚本包含的值放入任务变量中。我们还使用current传递当前请求项记录。我们只是在记录输出,但这些数据可以用于其他用途。
让我们再看看业务规则的样子,如图 6.7 所示:

图 6.7:记录活动请求项任务的业务规则
现在,让我们看看另一个计划任务的例子。
在这个例子中,我们将查看删除所有超过一年的事件。这种例子展示了我们如何在不同表中一夜之间删除旧记录。
让我们看看代码会是什么样子:
//Delete incidents that have not been updated in the last year.
var date = new GlideDateTime();
date.addYearsLocalTime(-1);
var delIncident = new GlideRecord('incident');
delIncident.addQuery('sys_updated_on', '<', date);
delIncident.deleteMultiple();
在此代码中,我们通过初始化一个新的GlideDateTime类来获取当前日期和时间,然后从该时间中减去一年。通过使用负数,在这个例子中是-1,我们从当前日期和时间中减去一年。使用GlideRecord查询,我们正在查找所有一年前更新的事件,并使用deleteMultiple删除它们。这删除了所有记录,无需查询Gliderecord。
这种类型的夜间工作对于清除旧记录或执行更新,为第二天做准备是非常好的。
图 6.8显示了计划任务本身的样子:

图 6.8:删除旧事件的计划任务
如前图所示,这个计划任务将在凌晨 1 点运行并删除夜间的事件。这是常见的做法,以便在工作日不使用系统资源。
最好的做法是在夜间运行长时间运行的脚本,这样它们就不会在业务时间影响实例资源。在全新的实例中还包括 ServiceNow 编写的夜间运行的任务;例如,import set deleter,它在七天之后清理导入集。
运行夜间脚本是一种使用计划任务的绝佳方式。这可以用于清理旧数据或设置报告,以便用户在早上第一时间可以使用。
现在,让我们看看一个工作流脚本示例。
在这里,我们将使用一个审批活动来添加一个由当前调用者负责的主任的审批。这涉及到遍历数据库中用户的主任,直到找到一个主任。
让我们看看代码:
var manager = 'current.caller_id.manager';
var title;
while (eval(manager) != '') {
title = manager + '.title';
if (eval(title) == 'Director') {
ans = manager + '.sys_id';
answer = eval(ans);
break;
}
manager = manager + '.manager';
}
在这个例子中,我们使用manager字符串不断添加.manager直到找到一个主任用户。这意味着说主任的主任,以此类推,直到找到主任。我们可以通过使用eval在循环中执行此操作以节省时间和资源。
eval评估括号内的内容,而不是将其视为其当前类型:在这种情况下,一个字符串。这允许我们通过点遍历找到用户的标题并获取如果找到负责人则的sys_id。这也是我们如何在每次循环运行时将.manager添加到字符串中的方法。eval在用脚本查找所需的字段并在找到后对其进行评估时非常有用。
如果找不到任何负责人,则在工作流程的这个阶段将不会添加任何批准,因为当我们到达组织树的顶端时,我们会遇到一个没有经理的用户并退出循环。
我们可以从工作流程中看到审批活动,如图 6.9 所示:

图 6.9:查找并添加用户的负责人作为审批人的审批活动
在我们的最终示例中,我们将查看一个脚本操作。
有时候我们想要挂起一个事件,但事件可能会被挂起很长时间。在这个例子中,我们将创建一个新的字段来保存事件保持挂起状态的日期和时间。一旦达到日期和时间,就会触发一个事件,该事件将运行我们的脚本操作,将事件从挂起状态移除,并将状态移动到进行中。
为了实现这个目标,我们还需要将事件设置为未来安排。在这里,我们将使用业务规则来完成这个操作。我们需要使用的代码如下:
(function executeRule(current, previous /*null when async*/) {
gs.eventQueueScheduled("incident.off.hold", current, current.sys_id, gs.getUserName(), current.u_on_hold_until);
})(current, previous);
在业务规则中,我们使用GlideSystem方法的eventQueueScheduled将事件放入系统调度器。我们在后续脚本中将第三个参数,或可以称为event.parm1,设置为当前事件的sys_id。您还会注意到最后一个第五个参数是我们自定义字段中保存我们希望事件保持挂起状态直到的日期和时间的值。
此方法将事件设置到系统调度器中,该事件将在我们在自定义挂起直到字段中设置的指定时间运行。我们正在触发的事件(incident.off.hold)是我们为特定功能创建的自定义事件。
一旦事件被触发,我们需要将事件从挂起状态移动到进行中状态。为此,我们将使用我们的脚本操作。
我们脚本操作中的代码如下:
var incident = new GlideRecord('incident');
incident.get(event.parm1);
incident.state = '2';
incident.u_on_hold_until = '';
incident.update();
我们使用在业务规则中设置的event参数,通过gliderecord获取我们想要更新的事件记录。一旦我们有了记录,我们可以将状态更改为进行中,值为二,并在更新事件记录之前重置挂起直到字段。
我们还可以在图 6.10 中看到完整的脚本操作:

图 6.10:移除事件挂起状态的脚本操作
这种业务规则和脚本操作的组合是了解在未来的指定时间运行脚本的能力的一个非常有用的技术。
这些实用的示例对于加强对这些更高级服务器端脚本技术的理解非常有帮助。
摘要
本章的主题是高级服务器端脚本。我们探讨了广泛使用的脚本包含以及使用计划任务、事件和脚本动作来安排脚本。我们还展示了如何将脚本作为 ServiceNow 工作流的一部分来编写,以及如何使用后台脚本来测试代码的各个方面。最后,我们提供了一些如何使用这些高级服务器端技术的优秀示例。
在下一章中,我们将探讨如何通过利用 UI 页面来构建自己的自定义页面。我们介绍了 Jelly 脚本以及它在 ServiceNow 中的应用,并展示了创建简单自定义页面的示例。
第七章:自定义页面简介
对于本章,我们将探讨如何构建自定义页面。这也会介绍 Jelly 代码的编写,以及何时何地使用它。我们还将看看如何构建一个自定义 UI 页面。
在介绍自定义页面时,我们将探讨以下主题:
-
Jelly 简介
-
UI 页面
-
自定义页面示例
Jelly 简介
Jelly 确实是一种不太为人所知的脚本语言。它并未出现在 ServiceNow 的主要部分,而是在平台的边缘。然而,如果你想在 ServiceNow 中创建自定义页面,学习 Jelly 是必须的。
很遗憾,Jelly 是一种不太为人所知的语言,因此关于如何使用它的文档也相对匮乏。由于开发者空间中普遍缺乏此类信息,使用已经创建好的 Jelly 页面或 ServiceNow 社区中的页面在入门时可以非常有用。
你会发现 Jelly 脚本的主要应用领域之一是 UI 页面,我们将在本章稍后探讨。Jelly 代码位于 XML 字段类型中,因为它是一个 Java 和 XML 脚本引擎,允许 XML 转换为可执行代码。
当查看 Jelly 脚本时,你会发现它几乎总是以相同的标签开始和结束。让我们看看这段代码:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" >
</j:jelly>
这段代码设置了 XML 版本和编码,然后编写了 Jelly 标签。Trim 设置了是否应该修剪此标签内的空白字符,我们将它设置为 false。其他四个属性设置了 Jelly 脚本的标签和阶段。
Jelly 被分为两个处理阶段。第一阶段标签是 j 和 g,第二阶段是 j2 和 g2。在第一阶段,j 和 g 标签被解析,结果被缓存,然后是第二阶段的 j2 和 g2 标签。当脚本随后再次运行时,只有第二阶段会被重新解析,使用第一阶段缓存的资料。
这意味着将不太可能改变的数据在第一阶段设置,而将不断变化的数据在第二阶段设置是一个好习惯。j 标签是 Jelly 的原生标签,而 g 标签是为 ServiceNow 特别创建的。
Jelly 是创建 UI 页面所需掌握的一项技能,我们将在下一章进一步探讨如何在 Jelly 中编写脚本。
UI 页面
UI 页面是存在于 ServiceNow 平台各处的脚本和 XML 页面。当你浏览 ServiceNow 时,你会看到许多 UI 页面,包括服务目录和知识的首页。UI 页面也是一种创建自定义页面的方式,你可以定义页面上显示的所有内容。
让我们看看如何创建一个新的 UI 页面。我们首先给我们的 UI 页面起一个名字;确保你记下这个名字,因为稍后你需要引用这个页面。分类字段主要用于参考,并不提供任何额外的功能。它可以帮助你组织你的 UI 页面。
UI 页面中可以包含代码的三个字段是:
-
HTML
-
客户端脚本
-
处理脚本
HTML 字段是 Jelly 脚本所在的地方,ServiceNow 为您提供了开始 Jelly 编码的标签:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" >
</j:jelly>
这些是我们介绍 Jelly 脚本时看到的标准标签,因此我们可以在 Jelly 标签内开始添加我们的 Jelly 脚本。
客户端脚本字段是一个脚本字段,可以在其中编写客户端代码。当 UI 页面加载时运行此客户端代码。此脚本可以放入 HTML 字段中,但如果将其分离到两个字段中,则更容易阅读。
处理脚本字段也是一个脚本字段,但这个字段运行服务器端脚本。脚本在页面提交时运行,因此在创建自定义页面上的表单时非常有用。
在 ServiceNow 中,您可以通过在实例 URL 的末尾添加 UI 页面的名称和.do来从任何地方访问 UI 页面。这意味着您可以非常容易地导航到您创建的 UI 页面。
让我们看看一个简单的 UI 页面,以展示如何将编写的代码显示在 UI 页面中。以下是 HTML 字段中的代码:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" >
Sample UI Page Text
</j:jelly>
使用此代码,我们将在 UI 页面的屏幕上简单地看到“Sample UI Page Text”文本。我们还可以在客户端脚本字段中添加一些代码,以查看代码运行时的情况:
alert('Page loaded');
我们在这里将只使用alert,因为我们是在客户端脚本中编写脚本以显示代码运行时的情况。
我们整个 UI 页面定义可以在图 7.1中看到:

图 7.1:Sample UI 页面定义
我们也可以通过导航到sample_page.do来查看此页面的外观,如图图 7.2所示:

图 7.2:Sample UI 页面示例
UI 页面是创建自定义页面并从平台上的标准 ServiceNow 表单和列表布局中脱离出来的绝佳方式。它们还为您提供了从头开始创建页面的机会,包含您喜欢的任何信息和数据。
脚本示例
现在我们已经看到了如何创建自定义页面,让我们看看如何制作一个工作 UI 页面的示例。
对于我们的示例,我们将创建我们自己的更改拦截器。这通常在创建新更改时看到,但在这个示例中,我们将创建一个 UI 页面作为要导航到的页面。
这是我们在 HTML 字段中将要使用的代码:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" >
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<input type="HIDDEN" id="sysverb_back"></input>
<h1 style="display:inline-block;" class="navbar-title">Change Request </h1>
</div>
<div class="nav navbar-right">
</div>
</div>
</nav>
<div class="container-fluid" style="border-bottom: 1px solid #ddd; margin-bottom: 5px;">
<h4 class="wizard-row-indent">What type of change is required?</h4>
</div>
<div class="container-fluid wizard-container">
<a href="change_request.do?sysparm_query=type=standard">
Standard Change - A predefined change. </a><br/>
<a href="change_request.do?sysparm_query=type=normal">
Normal Change - A regular change with over two weeks until start. </a><br/>
<a href="change_request.do?sysparm_query=type=emergency">
Emergency Change - A change occuring within two weeks. </a>
</div>
</j:jelly>
在这里,我们使用了 HTML 标签来创建我们自己的类似于更改拦截器的拦截器。这使得我们可以以任何我们想要的方式轻松地更改页面的各个方面,而无需使用 ServiceNow 拦截器模块。
通过查看更改拦截器的源代码,这里我们使用了实际拦截器中的代码,以保持页面的外观与 ServiceNow 保持一致。使您的自定义页面看起来像它们适合 ServiceNow 平台,通常会给用户带来良好的体验。
在我们的示例中,我们允许用户从三种变更选择中挑选一个,然后将他们重定向到该类型的新变更页面。在创建链接的标签中,我们可以看到每个选择的 URL 是如何变化的。我们使用sysparm_query在 URL 中设置新表单的值。在我们的示例中,我们使用它来设置变更类型。
让我们看看图 7.3中的 UI 页面本身:

图 7.3:拦截器 UI 页面定义
如果我们想在创建变更时使用此页面,我们可以在变更应用程序的“创建新”模块中修改,通过使模块导航到 URL change_interceptor.do 来导航到 UI 页面。
让我们看看图 7.4中的 UI 页面将是什么样子:

图 7.4:拦截器 UI 页面示例
我们可以看到页面上使用 ServiceNow 风格导航标题定义的三个选项。
摘要
在本章中,我们探讨了开始构建自定义页面。我们介绍了 Jelly 脚本、UI 页面以及 Jelly 在 UI 页面定义中的使用位置。我们还查看了一个 UI 页面示例,以了解其编写方式和完成后的用户界面。
在下一章中,我们将更深入地探索 Jelly 脚本,而不仅仅是基础内容。我们还介绍了 UI 宏,这是一种向 UI 页面和其他 ServiceNow 区域添加可重用组件和脚本的方法。我们还将通过一个实际示例查看如何将 UI 页面和 UI 宏结合起来使用。
第八章:使用 Jelly 脚本进行脚本编写
在本章中,我们将探讨使用 Jelly 进行脚本编写。我们将探讨如何编写 Jelly 代码来创建满足您需求的自定义页面和脚本。我们还将探讨 UI 宏以及如何在 ServiceNow 中创建它们,以及通过一个示例来进一步加深我们对这个领域的了解。
本章将涵盖以下主题:
-
Jelly 脚本
-
UI 宏
-
Jelly 脚本示例
Jelly 脚本
Jelly 脚本知识对于构建自定义页面和脚本非常重要,并且还可以修改 ServiceNow 中的 Jelly 代码,使其以不同的方式满足您的需求。我们在上一章介绍了 Jelly 脚本及其在 UI 页面中的应用。让我们回顾一下 ServiceNow 提供的 Jelly 标签的外观:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" >
</j:jelly>
一旦这些标签就位,我们就可以开始在我们的 Jelly 代码中包含它们。这可以添加到 UI 页面或 UI 宏中。我们将在本章后面讨论 UI 宏。
评估
首先,让我们看看 <g:evaluate> 标签。这个标签允许我们在标签内编写 JavaScript,并在需要时设置变量值。
<g:evaluate> 标签可以说是 Jelly 中使用最频繁的标签之一,也是我们需要掌握的标签之一。记住,我们可以根据希望脚本在哪个阶段运行来使用 g 或 g2。
让我们看看 <g:evaluate> 标签的实际应用示例:
<g2:evaluate var="jvar_variable">
var setVariable = 'Set variable to string';
setVariable;
</g2:evaluate>
在前面的例子中,我们将运行第二阶段的代码,因此我们在定义的标签中使用 g2。在标签定义中,我们还在定义一个变量名,用于与 var="jvar_variable" 一起使用。我们可以给变量取不同的名字,但我们必须始终在变量前加上 jvar 前缀,以便它能够正常工作。
在这里,我们将 jvar_variable 设置为脚本中的字符串值。在 evaluate 标签中,我们只需将表达式的最后一行设置为我们要设置的评估变量的值。在我们的例子中,我们使用了 setVariable,因此 jvar_variable 变成了 setVariable 的值,即我们的字符串。
在 <g:evaluate> 标签中,我们可以使用一些参数;让我们看看这些参数在另一个例子中的使用情况:
<g2:evaluate var="jvar_onHoldIncidents" object="true" jelly="true">
var holdIncident = new GlideRecord('incident');
holdIncident.addQuery('state', jelly.jvar_onHoldState);
holdIncident.query();
holdIncident;
</g2:evaluate>
在这个例子中,我们可以看到为 <g:evaluate> 标签新增的两个参数:object 和 jelly。object 标签决定了 jvar 变量是否应该被当作一个 object 处理。在我们的例子中,它将是 GlideRecord 查询,因此我们希望将其作为一个对象保留以供后续脚本使用。
另一个新增的参数是 jelly。如果将此参数设置为 true,则允许我们在脚本中使用 jelly 变量。在我们的例子中,我们使用的是 jvar_onHoldState 变量,我们假设它已经在之前的 <g:evaluate> 标签中设置为 3。我们需要将 jelly 参数设置为 true,以便我们能在示例脚本中使用这个变量。
如果
Jelly 脚本中的 if 标签与 JavaScript 中的 if 语句的工作方式类似。它用于在满足一组条件时运行代码。由于我们可以在 JavaScript 中设置 if 语句,因此您是否想使用 Jelly if 而不是 JavaScript if 取决于您自己。
我们可以使用 if 标签检查 GlideRecord 对象内部是否有任何记录。让我们看看如何使用我们的 evaluate 标签示例来完成这项工作:
<g2:evaluate var="jvar_onHoldIncidents" object="true" jelly="true">
var holdIncident = new GlideRecord('incident');
holdIncident.addQuery('state', jelly.jvar_onHoldState);
holdIncident.query();
holdIncident;
</g2:evaluate>
<j:if test="${!jvar_onHoldIncidents.hasNext()}">
No on hold incidents.
</j:if>
<j:if test="${jvar_onHoldIncidents.next()}">
There are ${jvar_onHoldIncidents.getRowCount()} incidents on hold currently.
</j:if>
在我们的示例中,我们根据实例中当前是否有挂起的工单来显示消息。if 语句有一个参数,即 test,这是我们需要评估为 true 的表达式,以便在标签中的脚本内运行代码。
这种类型的 if 语句有助于对 GlideRecord 对象运行条件。
UI 宏
UI 宏是可以在 UI 页面和其他 ServiceNow 平台上的其他区域中包含的脚本。通过将它们从单个 UI 页面中分离出来,这使得它们在整个平台上也易于重用。
UI 宏在 ServiceNow 平台上的各个地方都可以看到,运行服务目录购物车和审批摘要器。让我们看看它们是如何创建的。
UI 宏是一种相当简单的形式,只有几个字段。首先,我们需要给我们的 UI 宏起一个名字。之后,我们可以添加一个描述,然后完成 XML 字段。XML 字段是一个 XML 类型字段,其工作方式与 UI 页面上的 HTML 字段非常相似。我们用想要在运行此 UI 宏时运行的 Jelly 脚本填写 XML 字段。
我们还得到了在 XML 字段中创建新 UI 页面时给出的相同的 Jelly 代码:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" >
</j:jelly>
总结一下,这些标签引入了 Jelly 脚本,我们可以在 Jelly 标签内开始编写我们的 Jelly 脚本。
我们也可以通过使用 macro_invoke 标签从 UI 页面调用宏。让我们看看它是如何工作的:
<g:macro_invoke macro="kb_article_footer" />
我们使用 macro_invoke 标签和宏参数来调用我们的 UI 宏。我们只需要在宏参数中给出 UI 宏的名称,就像前面的例子一样。此示例将调用 kb_article_footer UI 宏。
同样,我们也可以在 UI 宏内部调用 UI 宏。为此,我们在标签内使用一个名为宏的 g 标签。我们可以在脚本中看到它是如何工作的:
<g:ui_button />
这将在 UI 宏内部调用 ui_button。
UI 宏是编写可以轻松在整个 ServiceNow 平台上重用的 Jelly 代码块的好方法。它们特别适用于添加到您的 UI 页面中。
脚本示例
在本章中,我们看到了如何在 Jelly 中编写脚本以及如何自己创建 UI 页面。让我们将这两种能力结合起来,制作一个可工作的 UI 宏。
这是我之前被要求的一个请求示例——在分组审批记录上的审批摘要。在这里,我们需要创建一个 UI 宏和一个格式化器,并将其添加到分组审批表单中。
让我们从我们的小组审批总结器代码开始,创建一个简短的摘要,我们将用它来批准我们的小组审批的任务记录:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" >
<tr>
<td class="label_left" width="100%">
<label style="margin-left: 12px"> Summary of Record being requested for approval:
<g:label_spacing/>
</label>
</td>
</tr>
<tr>
<td>
<table width="100%">
<tr>
<td class="label_left" width="150px">
<label style="margin-left: 10px">Short Description:
</label>
</td>
<td> $[current.parent.short_description]
<g:label_spacing/>
</td>
</tr>
<tr>
<td class="label_left" width="150px">
<label style="margin-left: 10px">Priority:
</label>
</td>
<td> $[current.parent.priority]
<g:label_spacing/>
</td>
</tr>
<tr>
<td class="label_left" width="150px">
<label style="margin-left: 10px">Opened by:
</label>
</td>
<td> $[current.parent.opened_by.getDisplayValue()]
<g:label_spacing/>
</td>
</tr>
<tr>
<td class="label_left" width="150px">
<label style="margin-left: 10px">Description:
</label>
</td>
<td> $[current.parent.description]
<g:label_spacing/>
</td>
</tr>
</table>
</td>
</tr>
</j:jelly>
如您所见,在这个例子中,代码量相当多,但其中很多是 HTML 标签,所以实际内容并不多。在这个例子中,我们创建了一个表格,其中包含了一些待批准记录的详细信息,以便当前的小组审批者可以看到他们正在批准的内容。
标签和间距是 HTML,您可以在 ServiceNow 为您提供的 UI 宏服务中找到这种脚本。代码的有趣之处在于设置任务值。我们使用美元符号来声明变量的开始,然后分别使用花括号或方括号来表示第一阶段或第二阶段变量。在这里,我们使用方括号来使用第二阶段,因为我们的数据将会变化,我们不希望它被缓存。
我们将这个 UI 宏命名为group_approval_summarizer;让我们看看它在图 8.1中的样子:

图 8.1:显示待批准记录摘要的 UI 宏
现在我们有了 UI 宏,我们需要构建一个格式化器来链接到 UI 宏,我们可以将其放置在小组审批表单上。格式化器需要一个名称,一个指向 UI 宏的链接,并且要在小组审批表中。我们可以在图 8.2中看到一个格式化器的例子:

图 8.2:用于显示 UI 宏的格式化器
现在我们有了格式化器,我们可以将其添加到小组审批表单布局中,以查看我们的 UI 宏。我们的小组审批表单上的 UI 宏结果可以在图 8.3中看到:


在前面的图中,我们可以看到我们的 UI 宏的输出。这个例子展示了您如何使 UI 宏在 ServiceNow 中增加表单的价值,添加您无法通过表单布局或设计师添加的额外数据。
摘要
在本章中,我们探讨了 Jelly 中的脚本。我们探讨了 Jelly 的各种标签以及如何将它们结合起来创建 Jelly 脚本。使用这些 Jelly 技术,我们探讨了如何创建包含 Jelly 脚本的 UI 宏,并看到了一个在 ServiceNow 平台上创建 UI 宏的实际例子。
在下一章中,我们将探讨 ServiceNow 中的调试。我们将查看 ServiceNow 提供的用于调试脚本、字段和您可访问的日志的调试工具,以帮助修复错误。
第九章:脚本调试
在本章中,我们将探讨调试。调试是创建有效的 ServiceNow 脚本的重要方面。我们将探讨脚本和会话调试器,以及它们是如何被使用的。然后,我们将探讨字段监视器、调试应用程序、JavaScript 日志和调试窗口。最后,我们将了解如何在 ServiceNow 实例中调试 REST 和 SOAP 消息。
在本章中,我们将探讨以下主题:
-
脚本调试器
-
会话调试器
-
字段监视器
-
调试应用程序
-
JavaScript 日志和调试窗口
-
调试 REST
-
SOAP 调试
脚本调试器
脚本调试器允许开发者调试服务器端脚本以发现代码中的错误或问题。我们在第五章,“服务器端脚本简介”中简要介绍了脚本调试器。
为了回顾,我们可以在我们编写的服务器端脚本中设置断点,以使用脚本调试器在脚本运行时进行调试。这是我们在 ServiceNow 中使用断点的唯一方法。
要使用脚本调试器,我们首先需要在我们的服务器端脚本中设置一个断点。这可以是任何您想要逐步执行的脚本,无论是系统脚本还是您自己构建的脚本。一个很好的例子是incident_events业务规则,因为我们还可以看到如何从事件表单触发不同的事件。
首先,我们在incident_events业务规则中添加一个断点,如图图 9.1所示:

图 9.1:添加了断点的业务规则事件
要设置此断点,我们只需在代码的边缘点击,使断点出现,如图中箭头所示。要删除它,我们只需点击蓝色箭头,断点就会被移除。一旦设置了断点,我们就可以打开脚本调试器。
要打开脚本调试器,我们可以导航到系统诊断|脚本调试器。这将在一个单独的窗口中打开脚本调试器。我们还可以从任何脚本类型字段打开脚本调试器。我们需要点击一个看起来像带有滚动条的小昆虫的脚本调试器图标,从这种类型的字段打开脚本调试器。
我们可以在图 9.2中看到这个图标被突出显示:

图 9.2:显示脚本调试器的脚本类型字段
现在我们可以通过更新任何现有事件来运行incident_events脚本,当我们看到图 9.3所示的窗口时,我们点击“开始调试”来查看在断点处停止的脚本:

图 9.3:脚本调试器弹出窗口
现在我们已经进入了调试过程,我们可以切换到调试窗口。从这里,我们可以看到屏幕右上角的一些相当标准的调试选项。
我们可以在图 9.4中看到这些:

图 9.4:脚本调试选项
我们用于调试的选项,从左到右排列:
-
暂停调试:暂停调试器会关闭调试器。这会将图标更改为电源图标,但通过点击这个更改后的图标,我们可以再次打开调试器。
-
恢复脚本执行:这将从当前断点之后恢复脚本,直到下一个断点,或者直到脚本完成。
-
跳过下一个函数调用:此按钮允许你遍历代码并跳过任何函数调用,这意味着跳过它们。
-
进入下一个函数调用:点击此图标将遍历脚本并进入代码中调用的任何函数。
-
退出当前函数:这将遍历当前脚本,但会退出代码当前所在的任何函数。
这些按钮允许你从断点的起点开始遍历你编写的代码,找到可能存在的问题。
这种调试方式在许多软件调试中很典型,提供了暂停、恢复和进入、退出、跳过函数的选项。然而,如果你对此不熟悉,花些时间熟悉这些按钮是有益的,因为它们可以在调试较长的脚本时节省你的时间。
让我们看看图 9.5中完整窗口的样子:

图 9.5:脚本调试窗口
这种调试方法非常有帮助,并且是 ServiceNow 早期版本中缺失的功能。有了脚本调试器,我们有了更全面的调试体验。
在脚本中添加断点并能够遍历它们,为调试增加了额外的层次,而不仅仅是记录输出。
会话调试器
会话调试器允许你在 ServiceNow 会话期间调试 ServiceNow 平台的一些方面。你还可以打开或关闭所有会话调试。会话调试会提供大量信息,但有时可能会信息过载,因此选择你想要特别调试的个别方面可能是有价值的。我经常发现安全调试是最有用的。
会话调试位于系统诊断中,包含多个模块。你可以通过点击你想要开启会话调试的模块来启用任何会话调试区域。你也可以通过选择“启用所有”来打开所有调试。不过,除了 UI 策略外,你不能关闭单个调试区域,调试是通过点击“禁用所有”或会话结束时禁用的。
我们可以在图 9.6中看到会话调试模块:

图 9.6:会话调试模块
当请求调试信息时,会话调试信息会出现在你加载的每一页上,通常在加载页面的底部。
值得注意的是,您可能需要向下滚动很多才能看到提供的信息,尤其是在较大的表单或列表中。
可以显示大量信息,这使得这种调试方法略显通用,并且不如更有针对性的调试方法强大。然而,这种调试方法可以提供在其他平台其他地方难以找到的信息。
让我们看一下一些示例会话调试输出。
首先,我们将查看安全。如果以登录用户身份登录时没有出现相关访问权限,ServiceNow 中的安全可能是一个棘手的问题。
由于 ServiceNow 中每种访问类型都可能存在多个访问控制,因此找到导致问题的确切规则可能很困难。
使用会话调试,我们可以看到哪些访问控制允许访问,哪些访问控制拒绝访问。这可以帮助我们确定我们正在查看的访问控制是否不正确并需要修改,或者是否存在我们未意识到的访问控制,这导致我们遇到访问问题。
在 图 9.7 中,我们可以看到事件表单上的会话调试输出:

图 9.7:安全会话调试输出
在这里,我们可以看到在加载事件表单时检查了事件表单的安全规则。如您所见,提供了大量信息,而这个示例只是加载事件表单提供的会话调试信息的一个小部分。
绿色勾选表示对安全规则的检查成功。在示例的底部,有一些较大的检查块,这些检查与特定的安全检查相关。我们可以看到在 PATH = 后的检查是正在进行的,因此,对于底部的检查,我们正在查看 record/incident.subcategory/read,这意味着我们正在查看登录用户是否可以读取事件记录上的子类别字段。在这种情况下,它是成功的,因此该字段显示给用户。
现在,让我们看看一些业务规则会话调试。
在 图 9.8 中,我们可以看到一个业务规则在事件表单上的调试示例:

图 9.8:业务规则会话调试
在这个业务规则会话调试脚本示例部分中,我们可以看到业务规则执行的日期和时间戳,以及业务规则开始和结束的位置。时间戳精确到千分之一秒,以便更容易确定执行顺序。
在您看到 >, 表示业务规则执行开始,而 < 则表示业务规则完成。我们还可以看到,在这个例子中,由于业务规则条件不匹配而跳过的业务规则,这可能很有帮助。
再次强调,前面的只是一个示例,展示了在开启业务规则会话调试时加载事件表单期间提供的信息,因此有大量数据需要搜索以找到你可能正在寻找的内容。
最后,我们将查看一个日志会话调试。
我们可以在图 9.9中看到一个日志会话调试的例子:

图 9.9:日志会话调试示例
日志将提供与记录打开相关的日志;在我们的例子中,打开事件表单。我通常不像其他人那样使用这个会话调试,但它可以帮助跟踪在记录打开时是否发生任何可能导致潜在问题信息的日志。
字段观察者
字段观察者调试功能允许你监视一个单独的字段,并监控该字段发生的任何更改。你一次只能使用字段观察者监视一个字段。如果你看到一个特定的字段正在更改,但不确定是什么修改了该值,这可以是一个有用的工具。
要监视一个字段,在表单视图中右键单击该字段,然后在上下文菜单的底部,你应该看到一个监视字段的选项。一旦选择,就会显示一个小虫图标,表示该字段正在被监视,并且从框架底部出现字段观察者窗口。
字段观察者框架显示字段的任何更改,并且使用字段观察者窗口中的复选框,你可以切换字段观察者将检查哪种类型的更改,例如,业务规则、客户端脚本等。
展示字段观察者实际操作的良例是观察一个事件上的优先级字段。如果你随后在表单上更改影响或紧急程度,你将看到字段观察者捕捉到优先级字段值的更改。我们可以在图 9.10中看到这一点:

图 9.10:字段观察者显示对优先级字段的更改
在前面的图中,我们可以看到,由于影响字段被更改为 1 - 高,优先级从 5 - 规划变为 3 - 中等。这一点在字段观察者和由此字段更改而运行的客户端脚本中被捕获。
如果你确切知道你感兴趣的哪个字段,并且想知道为什么值在变化,字段观察者可以是一个有用的工具。
调试应用程序
当调试应用程序时,我们通常对代码运行时特定时间点的范围感兴趣。一些用于调试应用程序的工具实际上是会话调试模块。我们在会话调试部分看到了这些模块,ServiceNow 类作为应用程序调试,以及其他一些模块。这些是:
-
调试业务规则
-
调试业务规则(详情)
-
调试安全
-
调试范围
业务规则和安全性无论我们是否在应用程序中都很有用,但对于应用程序编码来说,范围非常重要。为了展示范围调试的工作方式,我们可以在应用程序范围内创建一个测试应用程序及其业务规则。
一旦创建了应用程序和业务规则,我们需要在会话调试中点击调试范围和调试业务规则模块。例如,我们可以在会话调试日志中更新事件时看到这一点。
我们可以在图 9.11中看到这一点:

图 9.11:会话调试范围和业务规则
我们可以在日志中看到,当我们从测试应用程序进入业务规则时,我们也进入了测试应用程序的范围。一旦业务规则执行完成,我们就回到全局范围。
如我们在第五章“服务器端脚本简介”中看到的,当我们使用 GlideSystem 在应用程序中调试时,我们需要记住gs.log将不起作用。在应用程序中调试时,我们需要使用四个范围应用程序日志级别。
总结一下,这些范围应用程序日志级别是:
-
Error
-
Warn
-
Info
-
Debug
这些日志级别及其详细程度有助于在检查脚本中的值时将日志消息记录到系统日志中。
我们还可以为特定应用程序启用会话调试。这可以通过相关链接的应用程序页面来启用。如果应用程序脚本使用 GlideSystem 日志方法,它们将生成系统日志。
我们可以在图 9.12中看到这一点:

图 9.12:与应用程序相关的链接
要启用会话调试,需要点击相关链接。当调试活动正在进行时,相关链接将改变并显示为禁用会话调试。它可以再次被选中以禁用会话调试。
JavaScript 日志和调试窗口
JavaScript 日志窗口是查看 ServiceNow 平台中日志的另一种方式。与使用 GlideSystem 日志方法不同,这种方法允许你定义一种不同类型的日志发送到屏幕底部的窗口。
要查看 JavaScript 日志窗口,您可以访问您正在工作的实例的系统设置。一旦显示,选择开发者选项。从这里,将 JavaScript 日志和字段监视器属性设置为显示 JavaScript 日志窗口。
我们可以在图 9.13中看到设置的属性:

图 9.13:开发者系统设置显示 JavaScript 日志和字段监视器选项
一旦设置了 JavaScript 日志和字段监视器属性,你将在屏幕底部看到一个窗口出现。这个窗口与显示监视字段的窗口相同,但这次,设置的标签将是 JavaScript 日志。
我们可以在 图 9.14 中看到出现的窗口:

图 9.14:JavaScript 日志窗口
这个 JavaScript 窗口的示例没有显示日志,可以通过点击标题栏右侧的带有斜线的圆形按钮清除窗口中的所有日志。
我们还可以通过点击标题栏中的小、中、大按钮来更改窗口的大小。最右侧的向下箭头按钮可以关闭窗口。
在 JavaScript 日志窗口打开时,日志被添加到当前日志列表的底部。日志带有时间戳,这样您就可以看到日志的添加顺序。当在平台上打开表单时,查看客户端脚本运行的顺序时,这特别有帮助。
要将日志记录到 JavaScript 日志窗口,我们使用 jslog(),这是一个全局函数。这与 gs.log 的工作方式非常相似。我们可以在以下代码中看到如何使用 jslog 的示例:
jslog('Testing writing to the JavaScript log');
如果我们将前面的代码添加到 onLoad 客户端脚本中,我们可以在 JavaScript 日志窗口中看到日志。
我们可以在 图 9.15 中看到这一点:

图 9.15:带有示例日志的 JavaScript 日志
这种调试方法可以作为使用 GlideSystem 日志的替代方案。一些成熟的实例有时每秒会有很多系统日志,这可能会使调试变得困难,因此在这种情况下,JavaScript 日志窗口可能会有所帮助。
调试 REST
REST 消息对于在 ServiceNow 实例之间以及与第三方软件集成非常有帮助。在调试 ServiceNow 实例时,您可能还需要查看 REST 消息的日志。
为了调试 REST 消息,我们需要将系统属性 glide.rest.debug 设置为 true,该属性位于系统属性表(sys_properties)中。这个属性可能需要创建,因为它不包括在基线系统中。
让我们看看这个系统属性的示例:

图 9.16:系统属性 glide.rest.debug
在这个示例中,我们可以看到我们创建的系统属性。名称需要是 glide.rest.debug,值设置为 true。对于创建的任何系统属性编写良好的描述也是一个好习惯,以帮助其他管理员。
一旦这个属性设置为 true,当激活本章前面提到的会话调试日志时,REST 消息日志也将被添加到输出的日志中。一旦完成这种类型的调试,应该将此属性设置为 false 以避免耗尽系统资源。
SOAP 调试
与 REST 消息类似,SOAP 消息在与其他系统集成时非常重要。可能需要找出消息何时被接收以及确切的消息是什么。
为了调试 SOAP 消息,我们需要添加一个系统属性,如果之前已经添加过,则需要修改它。需要添加的系统属性是 glide.processor.debug.SOAPProcessor,其值需要设置为 true。
让我们看看这个系统属性的完成示例:

图 9.17:系统属性 glide.processor.debug.SOAPProcessor
示例显示了为调试 SOAP 封装需要创建的系统属性。名称必须准确无误,值设置为 true。一个良好的描述也将有助于其他人查看系统属性时解释其功能。
一旦设置了这个属性,我们将在系统日志中开始看到传入的 SOAP 封装 XML。这在查看实例在接收到消息时接收到了什么内容时非常有帮助。
调试完成后,应将此系统属性设置为 false 以停止实例中的过度记录。这也停止了在不再被监控时使用资源来提供这些日志。
摘要
本章的重点是调试。我们探讨了 ServiceNow 提供的各种调试代码的工具以及我们可以用来充分利用它们的技巧。我们探讨了脚本调试器和设置断点,会话调试模块和调试应用程序。我们还查看了一下 JavaScript 日志和字段观察窗口,以及这些如何帮助我们进行调试。
在下一章中,我们将探讨 ServiceNow 的最佳实践。这包括在客户端和服务器端编写和调试脚本时的最佳实践。我们还将研究这如何影响系统性能以及我们如何可以优化代码以最佳利用实例资源。
第十章:最佳实践
在本章中,我们将探讨在 ServiceNow 中脚本编写时的最佳实践。在脚本编写时,最佳实践非常重要,以便充分利用资源并提供最佳的用户体验。我们将探讨一般脚本、服务器端和客户端代码以及调试的最佳实践。我们还将深入研究涉及日志、队列和系统性能的最佳实践。
本章将探讨以下主题:
-
编码最佳实践
-
业务规则最佳实践
-
客户端脚本最佳实践
-
调试工具最佳实践
-
日志和队列最佳实践
-
系统性能最佳实践
编码最佳实践
在脚本编写时,有许多方法可以实现相同的目标,但保持代码高效可以使未来的维护和工作变得更加容易。通常也鼓励使用少量系统资源,因为这可以减少加载时间和云数据中心上的负载。
你经常会发现,较旧的实例由于在开发过程中没有遵守最佳实践而性能不佳。我之前曾参与过一些小项目,仅仅是为了使代码更高效,因为这些项目通常与缓慢的加载时间相关,因此对用户基础来说可能是一个巨大的改进。
在本节中,我们将探讨如何确保你的脚本遵守最佳实践以及如何避免常见的错误。
首先,一种相当常见的脚本类型是嵌套的 if 语句。这是一个在另一个 if 语句之后的 if 语句,通常用于捕获同一变量的几个潜在值。我们可以看到一个服务器端示例,看看它是什么样子:
var state = current.state;
if (state == '1') {
gs.log('State is New');
} else if (state == '2') {
gs.log('State is In Progress');
} else if (state == '6') {
gs.log('State is Resolved');
}
我们还可以在 图 10.1 中的脚本字段中看到它的样子:

图 10.1:嵌套 if 语句脚本示例
与使用嵌套的 if 语句相比,使用 switch 语句更好。我们可以看到之前的脚本可以是一个更高效的 switch 语句:
switch(state.getDisplayValue()) {
case 'New':
gs.log('State is New');
break;
case 'In Progress':
gs.log('State is In Progress');
break;
case 'Resolved':
gs.log('State is Resolved');
break;
default:
gs.log('Not Found');
}
对于可能需要维护 ServiceNow 实例的开发者来说,switch 语句也更容易阅读。
我们可以在 图 10.2 中看到脚本字段内的 switch 语句:

图 10.2:开关语句脚本示例
确保在每个 case 的末尾使用 break,以确保 switch 语句正确工作,因为有时可能会不小心省略。
业务规则最佳实践
业务规则最佳实践在很大程度上是关于在正确的时间运行业务规则。正如我们已经发现的,业务规则可以在表单保存或对记录执行操作的不同时间运行。
这意味着确保创建的业务规则以最有效的方式运行非常重要。正确使用业务规则,我们可以避免诸如更新同一记录两次和在不必要的时候运行脚本等问题。
我们将从查看业务规则上的复选框开始,如图图 10.3所示:

图 10.3:业务规则复选框
“插入”和“更新”是最常用的,它们经常一起使用。然而,确保您的业务规则仅在必要时运行是最佳实践。不要只是为了覆盖所有可能性而同时勾选两个框;如果业务规则在记录插入或更新时不需要,则不要勾选该复选框。
通常情况下,单独选择“删除”和“查询”,因为这些操作通常不会与其他操作对齐。
由于业务规则在服务器端运行,因此业务规则脚本中的良好做法也适用于 ServiceNow 的其他使用服务器端脚本的区域。
“当”字段是我们接下来要查看的字段。这是一个重要的字段,正确设置它可以帮助避免不良做法。这里有四个选项;让我们逐一看看。
之前
在记录保存之前运行业务规则是改变保存操作之前值的好方法。当对正在保存的记录进行更改时,应选择“之前”选项。这意味着如果您正在使用“current”来更改值,那么“之前”可能是最佳选项。
记住,在“之前”业务规则中运行脚本时,记录尚未保存。这意味着您只需要设置当前记录中的值,因为记录将在之后保存。这也意味着您需要确保避免使用current.update(),因为这会导致记录被保存两次。
之后
如果在运行业务规则时选择了“之后”选项,脚本将在记录保存后立即运行。如果我们正在更新当前保存的记录之外的值,但希望脚本立即运行,则应使用“之后”选项。
此选项适用于更新与正在保存的记录相关的记录,例如,在更新更改时更改任务。如果我们正在为正在保存的记录编写任何类型的审批更改脚本,这也是一个好的选项,因为这些更改将在记录重新加载后、保存后显示在记录上。
如果我们想要更新当前记录,那么使用“之前”选项会更好。如果您在脚本中考虑使用current.update(),那么考虑使用“之前”选项代替。
异步
“异步”选项会将脚本发送到系统调度器,并在记录保存后不久运行。当您编写的脚本不需要在记录保存后立即运行时,此选项是很好的选择。
异步选项的良好用途包括在集成之间发送消息和将任务发送到 ECC 队列。请记住,如果 ServiceNow 实例计划作业队列很长,异步选项可能会有更大的延迟,因此在设置异步业务规则时,请注意您实例的平均等待时间。如果您希望脚本立即运行,请考虑使用 after 选项。
显示
当记录在运行任何客户端脚本之前被加载时,显示业务规则会运行。这种类型的业务规则对于向客户端提供值非常有用,这样就不需要从客户端脚本调用服务器。我们可以使用这种类型的业务规则来避免 AJAX 调用。
显示业务规则在 ServiceNow 中并不总是可用,因此你可能会发现,在旧实例中使用它们可以减少客户端脚本中的服务器调用,从而减少记录加载时间。
如果你正在考虑在 onLoad 客户端脚本中使用 AJAX 调用,那么在可能的情况下,你应该使用显示业务规则来获取所需的服务器端值,并将它们设置在草稿本中供客户端脚本使用。
客户端脚本最佳实践
正如我们所见,客户端脚本在客户端运行,因此只能访问从服务器发送到客户端的数据。当我们编写客户端脚本时,我们的主要目标是尽量减少我们编写的脚本数量和对服务器的调用次数。
我们将客户端脚本保持到最小量的原因是因为这些脚本在用户面前运行,因此会直接影响加载时间。我们希望将加载时间保持在最低,因此客户端脚本也应该保持在最低。
当编写一个新的客户端脚本时,考虑脚本是否可以在服务器端运行是有价值的,如果是这样,那么这通常被认为是最佳实践。
让我们看看客户端脚本中最常见的坏做法之一。以下是一个示例脚本:
function onLoad() {
var user = new GlideRecord('sys_user');
user.get(g_form.getValue('caller_id'));
g_form.showFieldMsg('caller_id', 'Active: ' + user.active);
}
此脚本正在调用服务器以获取有关当前事件调用者的详细信息,以及该调用者是否现在在系统中活跃。这是一个不好的做法,因为我们可以在加载记录之前就已经知道这个用户是否活跃,而不是加载记录后再次回到数据库,这会增加加载时间。
我们可以在以下客户端脚本中看到这段代码的示例:

图 10.4:客户端脚本展示了不必要的服务器调用的不良做法
为了保持这一功能,同时改进代码,我们可以使用一个显示业务规则。通过这样做,我们在将数据从服务器传输到客户端之前,使用草稿本来获取我们想要的数据。
首先,我们使用以下代码创建显示业务规则:
(function executeRule(current, previous /*null when async*/) {
g_scratchpad.caller_active = current.caller_id.active;
})(current, previous);
此代码将活动字段值设置为caller_active草稿本,以便它可以被客户端代码使用。
让我们看看这作为一个显示业务规则的样子:

图 10.5:显示业务规则设置刮板变量
在我们的业务规则中,我们会将“何时运行”选项卡上的“何时”字段设置为显示,以便这个规则在表单加载时运行。在我们的例子中,我们将事件表单设置为运行此业务规则的表。
现在,我们已经设置了显示业务规则,所以我们只需要修改我们的客户端脚本:
function onLoad() {
g_form.showFieldMsg('caller_id', 'Active: ' + g_scratchpad.caller_active);
}
在早期代码中,我们使用在显示业务规则中设置的刮板值来设置字段消息文本。这意味着我们不再需要为这个细节进行服务器往返,因为它最初就被发送了。
让我们看看在定义的客户端脚本中这会是什么样子:

图 10.6:客户端脚本根据刮板值设置字段消息
在我们的例子中,我们使用脚本在事件表单加载时。这将显示在事件表单上当前选定的调用者的活动状态。我们还想注意,如果调用者发生变化,这个值可能会改变。
使用这种方法,我们还可以显示其他调用者属性,包括如电话号码或电子邮件地址等有助于联系调用者了解事件进展的辅助字段。我们还可以显示线路经理,并使用点遍历来显示一些经理的信息。
在这里,我们可以看到我们的示例中事件表单上调用者的输出会是什么样子:

图 10.7:事件表单显示调用者字段的自定义字段消息
我们已经打开了一个存在于 ServiceNow 中的基线事件,我们可以看到当前调用者在 ServiceNow 中是活跃的。
高度推荐审查你的 ServiceNow 实例中的客户端脚本,以确保它们没有进行不必要的服务器调用。记住尽可能使用显示业务规则。
调试工具的最佳实践
正如我们在前面的章节中所发现的,在 ServiceNow 中有很多调试方法。每个开发者都会有他们自己的首选方法,而且,也有更好的方法来调试不同的场景。
日志
我最喜欢的调试方法之一是使用日志技术,例如gs.log。在早期章节中,我们看到了这些日志可以被添加到脚本中,当脚本运行时,我们可以在 ServiceNow 实例的系统日志中看到这些日志。
然而,我们必须确保这种调试永远不会在生产实例中可见。检查你编写的每个脚本以确保没有调试代码留在最终版本中是一个好习惯。在执行新创建的脚本之前检查系统日志,完成后再检查一次是个好主意。如果有任何日志消息被发送,那么在将新功能发送到生产实例之前应该删除这些消息。
确保您的日志消息与您试图实现的功能相关也很重要。如果您打算在新的代码中保留一些日志功能,这一点尤为重要。在更大的应用程序或定制中构建一些日志功能可能会有所帮助。
例如,考虑以下代码和日志:
var caller = current.caller_id;
gs.log('log');
此示例将在调用者的值成功放入调用者变量时创建日志,但它没有提供太多信息。
我们可以看到这里创建的日志:

图 10.8:基本日志示例
为了改进我们的日志,让我们看看另一个例子:
var caller = current.caller_id;
gs.log('caller id obtained from the caller field');
第二个例子更好,因为我们现在有了关于代码此点发生的事情以及代码成功的地方的一些信息。
此日志的输出看起来像这样:

图 10.9:改进后的日志示例
尽管如此,我们还可以通过在此处添加更多信息来进一步改进它。
让我们进一步看看这种方式的日志记录示例:
var caller = current.caller_id;
gs.log('caller id obtained from the caller field with value ' + caller);
看看这个例子,我们现在在我们的日志中添加了代码来记录调用者字段的值,以及脚本中发生的情况的详细信息。这将记录系统日志中的用户sys_id,这样我们就可以检查调用者变量在此代码点的值是否正确。
我们可以在以下图中看到这个改进后的日志:

图 10.10:添加数据后的日志示例
现在,我们将看看一个最终的例子:
var caller = current.caller_id;
gs.log('caller id obtained from the caller field with value ' + caller.getDisplayValue());
在这个最终示例中,我们现在将显示值记录到日志中,这样我们就可以看到用户的实际名称,而不仅仅是用户的sys_id。这使得阅读日志语句的开发者看到的结果更清晰。
让我们看看最终示例在系统日志中的样子:

图 10.11:包含有用数据的日志示例
我们已经看到如何轻松地将日志添加到 ServiceNow 脚本中,但最佳实践是确保您创建的日志对其他开发者也有帮助。
会话调试
会话调试可以是一个强大的工具,但它也可能创建过多的日志,使调试变得漫长而艰巨。在使用会话调试时,重要的是要考虑您需要调试情况所需的日志类型。
虽然简单地启用所有会话调试可能看起来最简单,但最好只启用您感兴趣的区域。这确保了日志数量更少,意味着您可以更快地访问您感兴趣的信息。
通常,我发现除了作用域和安全规则之外,很少有开发者使用会话调试。这是因为其他方法更受其他调试选项会话调试的青睐。
日志和队列的最佳实践
日志和队列是 ServiceNow 实例的重要组成部分。日志在 ServiceNow 中用于记录实例中发生的事件。这些日志在调试中使用,正如我们所见,当包含这些日志时,它们应该被很好地定义。
队列在 ServiceNow 的多个区域中可见,从 ECC 队列到系统调度器。这些队列在适当的时间构建要执行的作业或操作。
在使用日志和队列时,重要的是使日志清晰,并且只在必要时在队列中创建作业或事件。
日志
需要为每个日志创建清晰的详细信息,并将其发送到正确的日志位置,以确保信息可以轻松读取。我们在调试部分介绍了某些日志技术或如何编写良好的日志,但我们还需要确保将日志发送到正确的日志本身。
当使用gs.log时,我们可以指定一个发送日志的源。如果没有指定源,则默认使用***脚本作为源。这将在脚本日志语句模块中显示日志。
在这里,我们可以看到一个示例日志,其中显示了***脚本源:

图 10.12:示例脚本日志
如果你正在创建一个较大的功能块或自定义应用程序,建议你创建自己的日志源。通过添加自己的源,你可以保持所有日志的独立性和易于维护。通常情况下,开发者会创建一个模块作为他们新应用程序的一部分,以便将日志定向到具有应用程序自定义源的日志。
队列
在 ServiceNow 中,你可以将作业和事件发送到许多队列。将任务或事件排队可以允许它们组合在一起批量处理,而不是单独处理。
每个队列将在给定时间后进行处理,每个队列的处理时间通常在系统属性中设置。改变队列的处理频率意味着任务或事件将更快地被处理,但会对系统资源产生消耗效应。在将队列处理时间设置为与基线系统提供的默认值不同的值时,始终需要仔细考虑这一点。
现在,让我们看看 ServiceNow 实例中一些最重要的队列以及每种队列类型的最佳实践。
事件队列
事件队列触发事件,然后由通知和脚本操作捕获。触发事件不会使用很多系统资源,因此通常不会成为问题。基线系统中提供了许多事件,因此此队列将始终包含活动。
设置脚本以将事件发送到事件队列的重要方面是确保事件被很好地定义,并且仅在需要时触发。
事件解释得很好的事件对其他开发者来说更容易使用,因此尽量在事件名称本身以及定义中演示事件是由什么触发的。
我们可以在这里看到示例事件日志:

图 10.13:事件日志示例
在事件日志中,我们可以看到事件的处理时间以及事件的处理持续时间。考虑事件处理时间过长的影响,并在必要时调查原因,这是值得考虑的。
在我们的示例中,我们可以在日志中看到一些常规事件,包括成功的登录和建立的会话,以及心跳事件和文本索引。
创建事件时,确保测试边缘情况,以确保事件的定义真正反映了它何时会触发。
当将事件发送到更远未来的事件队列时,请注意事件不会在表轮换过程中丢失。
系统调度器
您可以将作业添加到系统调度器中最常见的方法之一是使用异步业务规则。一旦异步业务规则被触发,它就会将作业发送到调度器队列以运行其中包含的脚本。
也可以将您自己的作业添加到系统调度器中。最好只在必要时这样做,因为调度器中的作业过多可能会损害实例性能。
ECC 队列
ECC 队列是存放来自 MID 服务器输入和输出消息的地方。这通常是为了实例和其他系统之间的集成。在这里,我们可以使用脚本向 MID 服务器发送作业。通常不会向 ECC 队列发送很多脚本消息,因为它通常会在一行中触发一次集成。
通过导航到 ECC | 队列,可以从应用程序导航器访问 ECC 队列。我们可以在这里看到此模块:

图 10.14:ECC 队列模块
队列通常会包含发送到 MID 服务器以检查它们是否响应的心跳探测。这是每 5 分钟进行一次,如果收到响应,则将 MID 服务器标记为在线,如果没有收到响应,则将 MID 服务器标记为离线。
当向 ECC 队列发送作业时,只需确保每个作业都是相关的,并且不要发送比必要的更多的作业。
系统性能最佳实践
有许多属性构成了实例的感知性能。这些可能会受到糟糕编写的脚本或大量不必要的脚本的影响。
脚本可能导致感知性能延迟的最常见方式之一是在表单打开时使用过多的客户端脚本。这会导致浏览器渲染和解析的延迟很大,用户可以非常明显地感觉到。正如我们之前提到的,在这种情况下的一种最佳实践是将创建的客户端脚本数量限制在仅必要的那些,并确保服务器调用保持在最低限度。
如果需要onLoad客户端脚本的信息,那么考虑使用显示业务规则而不是从客户端脚本调用服务器。
使用这种方法,我们可以在显示表单之前从显示业务规则中获取所需的服务器信息。然后,我们在客户端脚本中运行客户端代码,该代码将能够使用我们从显示业务规则中提供的信息。
通过导航到您的实例名称并在其后附加/cache.do,可以清除实例的缓存。这会为您清除缓存,并在您想要确保新脚本正在运行且缓存数据未被使用时有所帮助。
我们可以看到用户在导航到清除缓存页面时看到的屏幕,如图 10.15 所示。

图 10.15:缓存清除提供的信息
然而,清除实例的缓存可能会导致性能下降,因此了解何时进行此操作很重要。在非生产实例上,这通常不会成为问题,因为性能下降不应显著影响其他开发者。但对于生产实例,在可能的情况下应避免在办公时间内进行。
在考虑系统性能时,还值得检查正在使用的实例的性能。这可以通过 ServiceNow 提供的性能主页来完成。
让我们来看看这个主页提供的某些图表:

图 10.16:系统概述图表示例
在之前展示的图表中,我们可以看到概述、交易、响应时间和活跃会话图表。这些图表有助于了解实例当前的负载情况以及响应时间是否提供了良好的服务水平。
ServiceNow 性能主页还显示了比展示的更多的图表,您可以使用它们。我倾向于认为提供的响应时间是最有用的,但每个图表都有其用途。
通过关注实例性能,您可以主动解决潜在的系统性能问题。
摘要
在本章中,我们探讨了 ServiceNow 的最佳实践。我们探讨了在 ServiceNow 中使用脚本的一些最佳方法,以及一些应避免的坏习惯。我们的最佳实践包括编码、业务规则和客户端脚本领域,以及调试、日志、队列和系统性能。我们看到了这些最佳实践是如何协同工作以构建一个精简的 ServiceNow 实例的。
在下一章中,我们将探讨使用更新集。更新集是 ServiceNow 实例之间移动功能的重要组成部分,并且被广泛使用。我们将讨论何时以及如何使用更新集,以及它们与范围的关系。我们将发现如何在不同实例之间移动更新集,以及在此过程中应避免的陷阱。最后,我们将探讨使用更新集的最佳实践,以便您能够最大限度地利用 ServiceNow 应用程序。
第十一章:使用更新集的部署
在本章中,我们将探讨如何使用更新集。更新集是 ServiceNow 的一个重要部分,以最小的风险将新功能从非生产实例移动到生产实例。我们将探讨何时以及如何使用更新集,以及如何使用它们将功能从一个实例转移到另一个实例。之后,我们将探讨要避免的陷阱和更新集的最佳实践。
在本章中,我们将探讨以下主题:
-
何时使用更新集
-
如何使用范围与更新集一起使用
-
在实例之间传输更新集
-
更新集陷阱
-
更新集最佳实践
何时使用更新集
更新集在 ServiceNow 中用于将功能从一个实例移动到另一个实例。如果你在开发实例中进行了某些更改,你希望稍后将其移动到生产实例,那么建议使用更新集。
更新集捕捉你在实例内所做的更改,在它们被创建的同时。一旦你想要捕捉的所有更改都完成,更新集就完成了,并准备好移动到另一个实例。当移动到另一个实例时,可以预览更新集以检查更改是否与实例兼容,并提交以应用更改。
在对任何打算移动到另一个实例的更改进行更改之前,你的第一个任务应该是创建一个新的更新集来包含这些更改。在你开始进行更改之前,确保你正在更新集内工作。一旦更新完成,完成准备移动到另一个实例的更新集。
如何使用范围与更新集一起使用
当使用更新集时,我们需要记住我们当前正在哪个范围内工作。在我们在一个实例中使用任何类型的范围之前,我们处于全局范围,在我们在一个实例中创建任何更新集之前,我们处于默认更新集。
如果我们不在创建的更新集中,那么我们也将处于默认更新集中。然而,ServiceNow 实例中的每个范围都有一个不同的默认更新集。了解这一点很重要,这样如果确实切换了范围,那么你的更新集也会自动更改。
如果我们在实例中更改我们正在工作的范围,我们可以在更新集选择器中看到这种变化。在图 11.1中,我们可以看到一个全局范围内的实例:

图 11.1:全局范围内的应用程序和更新集选择器
因为我们处于全局范围,所以为我们选择的默认更新集是全局范围的默认更新集。我们可以通过服务现在实例在更新集选择器中显示的方括号内的文本看到这一点。
如果我们只更改应用程序,因此范围,我们也可以在更新选择器中看到变化。让我们以将应用程序更改为引导设置为例,以查看变化。
我们可以在图 11.2中看到这些更改:

图 11.2:在引导设置范围内应用和更新集选择器
如您所见,更新集会自动为我们更改。这意味着一个实例中可能有多个不同的默认更新集,并且每个应用程序都将有自己的更新集。
这个例子很好地引出了关于在不同范围内使用更新集的另一个观点。每个更新集只能存在于一个范围内。在创建更新集之前,请确保您当前处于正确的范围内,以便您所做的更改可以添加到更新集中。一旦创建,ServiceNow 安全性将阻止任何人向更新集的应用程序字段写入。这是由安全规则设置的,不应修改。
在实例之间传输更新集
一旦更新集完成,它就准备好转移到另一个实例。在实例之间传输更新集需要几个步骤,一旦最初设置好,它就可以是一个快速的过程。
要开始在不同实例之间移动更新集的过程,我们首先需要登录到我们想要移动更新集到的实例,即目标实例。然后我们导航到系统更新集 | 更新源并点击新建。
我们可以在图 11.3中看到新的更新源屏幕:

图 11.3:新的更新源表单
在此表单中,有一些关键字段需要填写。让我们先从必填字段开始:名称和 URL。名称字段正是如此——为我们的更新源提供一个名称,以便系统管理员可以轻松识别此更新源。这通常是我们要从其中获取更新集的实例名称或源实例。URL 字段需要是源实例的 URL,格式为 <instance_name>.service-now.com。
此表单上的其他重要字段是用户名和密码字段。这些需要是源实例的用户名和密码,并且该账户必须是该实例的管理员账户。
类型字段和简短描述字段也可以填写以添加额外的标签来帮助识别更新源。一旦表单填写完成,点击测试连接以确保可以访问源实例。如果连接失败,您可能需要检查您提供的信息并确保源实例可以访问且没有可能导致失败的任何 IP 访问控制。
如果连接成功,则可以保存表单。一旦保存了更新源,我们就可以开始从源实例引入更新集。现在有一个相关的链接“检索完成更新集”可供点击。一旦选择,所有完成的更新集将被转移到目标实例。
请记住完成您想要移动到另一个实例的所有更新集,因为不完整的更新集将不会移动。
要查看已移动到目标实例的更新集,我们可以导航到系统更新集 | 获取的更新集。现在更新集已存在于目标实例中,我们还需要将自定义更改应用到目标实例。
获取的更新集应该已经被自动预览;然而,在较旧的实例中,这可能不是情况,可能需要手动完成。有时预览可能会引发一些警告或错误,在提交更新集之前需要审查。这可能意味着缺少表或目标实例上存在特定自定义的新更新。大多数时候,这些错误和警告最终都会导致远程更新被接受和提交,但请审查每一个以确保您应该提交所有更新。
预览过程完成后,您可以提交您的更新集。这将把源实例中的所有自定义更改添加到目标实例中,排除在预览过程中标记为跳过的任何更改。
提交完成后,您已成功将更新集从一个实例转移到另一个实例。
更新集陷阱
更新集是移动实例之间配置的绝佳方式。然而,在使用更新集时,有一些陷阱很容易陷入。
大多数开发者面临的主要问题是更新集中将添加哪些自定义更改。并非所有对表的更改都会添加到更新集中。一个表中的更改是否添加到更新集中取决于表中的记录是否被视为数据。被视为数据的表不会被添加到更新集中;否则,更新集将充满不必要的记录。
例如,如果事件表包含在更新集中,那么在开发实例中修改或创建的事件将被移动到生产实例中,这并不是一个好主意。大多数表如预期的那样包含或排除在更新集中,但有些可能并不总是明显,因此值得检查。
为了避免在我们的更新集中遗漏更新或包含我们不想要的更新,我们可以检查我们正在处理的表,并将更新发送到更新集。为此,我们可以查看我们正在处理的表的字典条目。
在一个表的字典条目列表中,我们关注的是类型为集合的记录。这给出了表的定义,并持有表的属性,这是我们需要的。向表中添加更新到更新集的属性称为更新同步,它需要设置为真。通常,如果已添加此属性,它将被设置为真,如果省略则将为假。
让我们看看图 11.4中所示的事件表的字典条目:

图 11.4:事件表的字典条目
由于事件表包含事件数据,我们不希望我们的更新集中有事件记录。正如我们所见,更新同步属性在这里没有出现,因此我们创建或修改的任何事件都不会出现在更新集中。
现在让我们看看客户端脚本的字典条目。我们可以在图 11.5中看到这一点:

图 11.5:客户端脚本表的字典条目
在客户端脚本字典条目的属性中,我们可以看到更新同步属性,并且它被设置为 true。这意味着客户端脚本记录将被添加到更新集中。
一个通常被认为包含在更新集中但实际不包括的表是计划任务表。由于大多数包含脚本的记录都被添加到更新集中,这往往会让开发者感到意外。可以通过将其导出为 XML 并将其导入另一个实例来移动计划任务。
相比之下,许多开发者没有意识到将被添加到更新集中的表是系统属性表。有时开发者实例的设置,如设置为停止发送通知的通知,可能会意外地转移到生产实例。这是我多年来看到的一些陷阱。
更新集最佳实践
当使用更新集时,有一些最佳实践我们可以遵循,以确保我们不会向我们的生产实例添加不良的自定义设置,并充分利用更新集。
首先,在完成更新集之前检查其中包含的更新总是一个好主意。有时开发者可能会无意中向他们的更新集添加他们本不想添加的更新。因此,检查更新集中包含的每个单独的更新总是一个好主意。特别是,要特别注意任何操作为DELETE的更新,因为这可能更难撤销。
我们可以在图 11.6中的更新集中看到一些示例更新:

图 11.6:包含示例更新的更新集
我们可以在相关列表的客户更新选项卡中看到这个更新集中的更新。在更新集示例中,我们可以看到三个更新,包括两个客户端脚本和一个业务规则。我们需要确保我们看到的每个客户更新都是我们想要包含在这个更新集中的更改,并且没有意外包含额外的更新。
在操作列中,显示在提交此更新集时将执行的操作。在三个更新中,我们特别关注具有 DELETE 操作的更新,以确保确实需要执行此操作。一旦记录被删除,撤销操作可能更困难。插入或更新是一个更容易更改或回滚到早期版本的更新。
为你的更新集制定一个命名约定也是一个好主意。起初,当更新集数量较少时,这似乎是不必要的;但随着实例的成熟和更新集数量的增加,这可以非常有帮助。命名约定不需要复杂,只需保持一致即可。常见的命名约定可以是发布、冲刺,或者是故事或缺陷的记录编号。
命名约定的几个例子包括:
-
Description - Release - Date
-
Story/Defect: Developer
-
Sprint/Month
在决定命名约定时,要确定与你的流程和实例相关的细节,并确保遵守这一约定。
如本章前面所述,在设置更新源时,我们需要为从该实例获取更新集的实例提供一个管理员账户的用户名和密码。确保用于更新源的账户细节不会频繁更改是一个好的做法,因为这将阻止更新源从该实例拉取更新集。
使用更新集时,确保在生产实例中提交的更新集是在适当的时间进行的,这是一个好的做法。与对生产系统的任何更改一样,都存在一定程度的风险。这意味着如果可能的话,应该在业务时间之外提交更新集,或者至少在安静的时间段,以防出现任何问题。
Summary
本章我们探讨了更新集。我们看到了如何使用更新集以及它们如何与不同的应用程序范围一起使用。在本章中,我们还探讨了在实例之间转移更新集、避免更新集的陷阱以及使用更新集的最佳实践,尤其是在检查每个更新集更新时。
在最后一章中,我们将利用所学知识在 ServiceNow 中构建一个自定义应用程序。我们将探讨如何创建自定义应用程序,特别是如何在自定义应用程序中使用脚本。我们将发现自定义应用程序的端到端开发,以及如何测试和部署我们所制作的自定义应用程序。
第十二章:使用 ServiceNow 脚本构建自定义应用程序
在最后一章中,我们将探讨使用 ServiceNow 脚本构建自定义应用程序。自定义应用程序是创建 ServiceNow 产品中未包含的功能的绝佳方式。我们将探讨创建自定义应用程序,并将脚本作为应用程序的一部分使用。在探索如何测试和部署我们的新自定义应用程序之前,我们还将学习自定义应用程序的端到端创建。
在本章中,我们将探讨以下主题:
-
使用 ServiceNow 脚本创建自定义应用程序
-
端到端开发
-
测试
-
部署
使用 ServiceNow 脚本创建自定义应用程序
在 ServiceNow 中构建自定义应用程序的能力使 ServiceNow 成为一个如此通用的产品。由于 ServiceNow 最初作为服务台解决方案推向市场,开发者们探索了该平台的可能性,从那时起,它已被用于商业的各个领域。
然而,仍有许多使用 ServiceNow 的方法不包括在基线系统中,为此,我们可以创建一个自定义应用程序。
在创建自定义应用程序之前,值得考虑的是否自定义应用程序将提供最佳解决方案。有时,我们可以使用现有的 ServiceNow 应用程序来执行我们想要实现的功能,直接使用它或对其进行轻微修改以满足我们的需求。如果是这种情况,那么考虑不创建自定义应用程序并使用该平台可能是有价值的。
如果您需要的功能需要对现有应用程序进行大量修改,或者在那个阶段根本不存在,那么创建一个自定义应用程序是明智的选择。
创建应用程序
一旦我们创建了一个新的应用程序,我们就可以开始向该应用程序添加应用程序文件。我们创建的应用程序文件成为应用程序的一部分。
要创建一个新应用程序,我们可以导航到系统应用程序 | 应用程序,我们将看到在此实例上创建的当前应用程序。然后我们需要点击如图12.1所示的右侧的“新建”按钮:

图 12.1:实例应用程序页面
一旦选择了“新建”按钮,您将获得一系列关于创建应用程序时希望从哪里开始的选择。
我们可以在图 12.2中看到提供的选项:

图 12.2:应用程序创建选项
从这一系列选择中,我们可以决定从哪个起点创建我们的应用程序。前两个选择通常是最常用的。从头开始正是如此,只创建应用程序和新的应用程序范围。不会为您创建任何表格、模块或访问控制。
创建自定义应用程序将引导你创建表格、模块和访问控制。之后,与从头开始创建大致相同。
根据我的经验,第三个选项,即从服务开始,很少被使用,但允许开发者增强由服务创建器创建的服务,并在完成后可能覆盖它。
此处的最后一个选项不是作为基线系统的一部分提供的,但在服务管理激活时可以使用。此选项同样使用得不多,尽管它允许从现有流程创建。
当选择从模板开始时,我们会看到一个额外的页面,我们可以在图 12.3中看到:

图 12.3:应用程序创建模板选项选择
从这里,我们既可以创建一个预定义的模板,也可以进一步配置设置以创建应用程序。
现在自定义应用程序已经创建,我们可以开始添加应用程序文件并构建应用程序。
构建应用程序
一旦应用程序创建完成,我们就可以开始使用应用程序文件构建应用程序。这些文件组合在一起构成了应用程序。当应用程序创建时,也会为应用程序创建一个应用作用域。在创建应用程序时,你也会被置于新的应用作用域中,因此了解这一点很重要。
当你创建一个新的更新集到一个新的默认更新集,以适应你现在所在的应用作用域时,你也会看到你的更新集已经改变。由于你现在处于新应用程序的应用作用域中,任何所做的自定义都将添加到你的新应用程序中。
为了跟踪当前的作用域和更新集,在开发时显示应用程序和更新集选择器可能很有用。这两个选项可以在系统设置的“开发者”标签中设置,如下图所示:

图 12.4:开发者系统设置显示应用程序和更新集选择器
通过在图 12.4中设置这两个选择器选项,我们随后在屏幕顶部的标题栏中看到了当前的应用作用域和更新集。这些显示在一个下拉字段中,因此如果需要,两者都可以轻松更改。
我们可以看到在标题栏中显示的两个选择器下拉字段:

图 12.5:应用程序和更新集选择器
这些选择器字段在你需要在更新集之间工作或在你当前开发中的多个作用域工作时特别有用。
当在多个标签页中工作时,刷新浏览器页面可能是一个明智的想法,以确保你正在工作的标签页上的选择器是最新的。
现在我们已经创建并开始构建我们的应用程序,我们可以继续进行应用程序的开发。
示例应用程序
在本章中,我们还将构建一个示例自定义应用程序,以巩固我们学到的理论。对于我们的示例,我们将构建一个用于存储用户访问数据的程序。
首先,我们需要创建应用程序。从我们在图 12.2中看到的选项中,我们将选择创建自定义应用程序。一旦我们选择了这个选项,我们就得到了图 12.6中显示的表单:

图 12.6:创建自定义应用程序表单
在此表单中,我们需要为我们的应用程序提供一个名称,然后其余字段将被填写。我们可以更改字段中的值,但在范围字段中,前缀必须是您公司或开发者实例的前缀。
对于我们的示例,我们还想创建一个表,因此我们还需要检查创建表选项。这为我们提供了一些额外的表字段需要填写。这些字段将为您自动填充,但您可以根据需要更改它们。
在这里,我们可以看到我们用户访问应用程序的填写好的表单。自定义应用程序前缀以x_开头,后跟您或您公司的特定前缀。此范围是必填的,且仅针对您。图 12.7中的空白处是显示此前缀的位置:

图 12.7:完成的自定义应用程序表单
现在我们已经完成了表单,我们点击表单底部的创建按钮。
在创建应用程序和新表之前,我们被提供了一个确认弹出窗口,如下所示:

图 12.8:自定义应用程序创建确认弹出窗口
对于我们的示例,我们将点击确定并创建应用程序。一旦应用程序创建完成,我们将得到一个按钮来编辑应用程序,该按钮将打开开发工作室,我们将在下一节中对其进行更多探索。
我们已经成功创建了我们的应用程序,将在下一节中继续介绍用户访问应用程序的示例。
端到端开发
一旦在实例上创建了一个应用程序,我们就可以继续开发应用程序。当我们处于应用程序的作用域时,所有自定义设置都将添加到应用程序中。我们可以像通常在应用程序外进行开发一样更改实例,例如创建和修改脚本,或者我们可以使用 ServiceNow 为我们提供的工作室。
工作室
工作室是由 ServiceNow 创建的,用于创建自定义应用程序时使用。一些开发者更喜欢工作室,因为它在一个地方显示了应用程序,而其他人则更喜欢像在全局范围内一样进行开发。
要访问工作室,请导航到系统应用程序 | 工作室。这将打开浏览器中的一个新标签页,并给我们机会加载当前实例上创建的一个应用程序。
我们可以在图 12.9中看到加载应用程序表单:

图 12.9:工作室加载应用表单
在列表中,您将看到您正在工作的实例上可加载的应用。一旦我们在工作室中加载了想要工作的应用,我们就可以在左侧看到当前的应用文件。我们可以看到图 12.10中显示的屏幕:

图 12.10:测试应用的示例工作室
例如,为这个测试应用创建了一个业务规则和客户端脚本。
当我们有一个新应用时,我们需要创建一个新的应用文件来开始。要创建一个新的应用文件,我们可以点击前图中可以看到的创建应用文件按钮。
我们随后会看到创建应用文件屏幕,如图图 12.11所示,在这里我们可以选择在应用中创建哪种类型的应用文件:

图 12.11:应用文件创建屏幕
有许多应用文件类型可供选择,其中一些更常见的是客户端脚本、业务规则、访问控制和表。一旦选择了应用文件,点击创建即可开始编辑应用文件。一旦应用文件完成并保存,它将出现在工作室左侧的窗格中。
使用工作室的主要好处之一是您可以在左侧窗格中非常容易地跟踪所有应用文件,并通过点击它们来修改它们。另一个重大好处是能够使用标签快速在不同的应用文件之间切换。我们可以在图 12.12中看到这一点:

图 12.12:工作室标签页开发示例
如前图所示,我们可以为不同的应用文件打开多个标签页。这可以是新文件或现有文件,这无疑是我在使用工作室时喜欢的一个特性。
工作室非常适合将应用的所有方面汇总在一起,但是否使用它取决于开发者的个人喜好。一些开发者对其深信不疑,而另一些则根本不会使用它,还有一些开发者处于中间状态。
应用开发
无论是否使用工作室,一旦创建了一个应用,我们就需要进行开发并确保创建了我们应用所需的所有应用文件。每个应用都是不同的,在 ServiceNow 中创建独特应用的能力几乎是无限的。
在开发您的应用时,请记住本书早期章节中提到的技能和技术,以充分利用它们。
当创建一个新应用时,我建议首先考虑应用的数据表结构及其关联关系。一旦数据表结构就绪,其他应用文件将更容易规划并放置到应用中。
ServiceNow 还允许我们将被视为数据的记录添加到我们的自定义应用程序中。这使得我们可以将之前章节中发现的未设置为 true 的更新同步属性记录传递过来。为此,我们可以在列表视图的标题栏上右键单击,然后我们会得到创建应用程序文件的选择,如图 图 12.13 所示:

图 12.13:从列表视图创建应用程序文件
在 图 12.13 中,我们正在查看一个由于筛选而列表中只有一个记录的事件列表。在选择创建应用程序文件选项后,我们将得到一个弹出窗口来选择如何添加应用程序文件。此弹出窗口可以在 图 12.14 中看到:

图 12.14:创建应用程序文件弹出窗口
在 图 12.14 中的这个弹出窗口有一个“加载时”字段,这是此表单上最重要的字段。从这里,我们有以下三个选项可以选择:
-
新安装和升级
-
新安装
-
新安装带示例数据
前两个选项相当直接:数据要么仅在应用程序的新安装中加载,以后不再加载,要么在新安装和未来的应用程序升级中加载。第三个选项略有不同,但大多数系统管理员对示例数据的概念都很熟悉。使用此选项,记录将在应用程序部署时可用,尽管这不是强制性的。当我们在本章后面讨论部署时,包括示例数据的相关选项将变得清晰。
一旦选择了“加载时”字段,我们就可以点击“确定”,记录将被添加到我们的应用程序中。我们可以在 图 12.15 中看到我们的测试应用程序和之前的示例的这种样子:

图 12.15:显示在应用程序相关列表中的应用程序文件示例
在 图 12.15 中,我们可以看到之前在我们的列表视图中显示的事件记录已经被移动到我们的自定义应用程序的应用程序文件中。
在添加了您申请所需的所有申请文件之后,我们就可以开始测试应用程序了。
示例应用程序
现在我们已经了解了如何开发我们的应用程序,我们也可以开发我们的示例应用程序。我们的示例应用程序用户访问已经创建,我们可以按常规方式开发或使用工作室创建新的应用程序文件。
从简单地创建我们的应用程序开始,我们就会看到一些应用程序文件作为我们应用程序的一部分,这些文件将在工作室或应用程序定义中显示。
现在我们可以看到以下图中我们的应用程序在工作室中的样子:

图 12.16:用户访问应用程序的工作室
我们已经可以看到,通过创建我们的应用程序,我们已经有一个表、角色、一些访问控制、应用程序菜单和模块。所有这些都在我们的应用程序范围内创建。
现在,我们需要创建另一个表,以在用户和访问之间起到类似多对多关系表的作用。我们可以创建一个m2m表,但这次我们将创建一个普通表,因为我们不希望在表之间有编辑按钮的相关列表。我们可以通过导航到系统定义 | 表与列并按常规方式点击创建表来创建此表,并且它也将添加到应用程序中。
我们现在需要两个表,因此我们将向用户访问表添加一些参考字段,用于用户和访问。访问字段链接到我们的另一个新应用程序表。
我们现在可以看到表单的样子,其中包含一些示例数据,如下所示:

图 12.17:用户访问自定义表视图
我们还需要在我们的访问表中添加一个名称字段,以便我们可以识别每个访问项,并且我们将添加一个相关列表来查看具有该访问权限的所有用户。
我们现在可以看到这个表单的样子,再次使用一些示例数据,如下所示:

图 12.18:访问自定义表视图
我们用户访问应用程序的表结构现在已完成。现在应用程序中将使用三个表,分别是用户表、提供基线 ServiceNow 的用户表、访问表和用户访问表。
我们已经创建了一种跟踪 ServiceNow 中每个用户访问权限的方法。通常,首先进行自定义应用程序的表结构是一个好的做法,因为任何脚本或关系都更容易为应用程序规划。在应用程序中后期添加表可能会引起问题或导致某些应用程序的大量返工。
我们还想要确保一旦用户在我们的应用程序中获得了访问权限,我们就停止该用户被选中进行相同的访问,这意味着需要创建一个脚本化的高级参考限定符。
首先,我们将在高级参考限定符中添加一些脚本,以调用脚本包含。这个参考限定符将用于用户访问表上的用户字段:
javascript:new userAccessRefQual().stopDuplicateUserAccess(current.access);
现在我们有了调用脚本包含的引用限定符,我们需要创建脚本包含和输入代码,以返回没有当前选中访问权限的用户。
我们脚本包含的代码将看起来像这样:
var userAccessRefQual = Class.create();
userAccessRefQual.prototype = {
initialize: function() {
},
/* Returns users that do not currently have this access
variables:
access - sys_id of the current access
*/
stopDuplicateUserAccess: function(access) {
var usersWithAccess = [];
var accessRec = new GlideRecord('x_152110_uaccess_user_access');
accessRec.addQuery('access', access);
accessRec.query();
while (accessRec.next()) {
usersWithAccess.push(accessRec.user.toString());
}
return 'sys_idNOT IN' + usersWithAccess.toString();
},
type: 'userAccessRefQual'
};
此代码将检查所有当前用户访问记录,确保返回给选定的用户是目前没有此访问权限的用户。
这通过返回字符串sys_idNOT IN和填充了当前对表上的当前访问有权限的所有用户的数组的全部内容来完成。这可能是一个非常有助于过滤掉我们不希望显示给用户的记录的技术。
我们可以在下面的图中看到创建的脚本包含:

图 12.19:提供高级引用限定符功能的脚本包含
现在我们已经完成了脚本并设置了表格,我们在开发方面很高兴完成我们的应用程序。当然,我们还可以添加更多功能来增强这个应用程序,比如添加额外的验证、脚本或字段到表格中,但我们将在这个例子中完成应用程序。
测试
任何自定义应用程序的一个重要部分是测试它以确保其正确运行。有错误或功能不佳的应用程序可能会迅速让客户对应用程序感到失望,并且通常应用程序将不再被使用。因此,在部署之前彻底测试任何应用程序至关重要。
在测试新的自定义应用程序时,测试应用程序本身以及围绕应用程序的部分同样重要。这意味着不要只是测试应用程序是否工作,还要测试任何与新的应用程序链接或相关的应用程序是否仍然以之前的方式工作。这在处理访问控制和安全问题时尤为重要,并且始终确保没有授予不应该有的访问权限,并且现有应用程序的用户不会失去他们之前可以访问的数据。
如果我们在进行测试时遇到问题,值得记住我们在第九章,“调试脚本”,中学到的调试应用程序的技术。我们可以使用会话调试工具,包括调试作用域,来解决我们遇到的问题。
也有以下作用域的应用程序日志级别选项来修复问题:
-
错误
-
警告
-
信息
-
调试
这些日志对于找出问题所在可能很重要,并且记住gs.log在自定义应用程序作用域中不会工作。
在测试自定义应用程序时检查系统日志是一个好习惯,以确保没有留下不应该有的日志消息。我们需要确保这些日志消息在没有必要的情况下不会被部署到生产实例中。
示例应用程序
现在开发已经完成,对于我们的示例应用程序,我们将查看如何对其进行测试。我们需要关注的应用程序的主要方面是我们创建的脚本。
然而,检查您应用程序的每个方面始终很重要,特别是要对照应用程序将面向的利益相关者的要求进行检查。
已经添加了一些虚拟数据,以便我们可以检查我们的脚本在用户访问和访问表中是否正确工作。用户表填充了 ServiceNow 基线系统中可用的用户,您可以在测试应用程序时使用这些用户。
我们可以在以下图中看到用户访问表中的条目:

图 12.20:用户访问自定义表列表视图
如图 12.20所示,有两个用户可以访问 Server A100。这意味着如果我们尝试在选择了 Server A100 时选择这些用户,我们不应该能够选择这些用户。
让我们检查这是否可行:

图 12.21:用户访问自定义表表单测试用户字段引用限定符
如前图所示,在搜索Fred后,我们没有看到Fred Luddy,这是一个好兆头,它告诉我们我们的脚本引用限定符正在正确工作。
作为测试的一部分,我们还将从Fred Luddy那里移除对 Server A100 的访问权限,并检查他在为 Service A100 创建用户访问记录时是否可供选择。使用我们创建的代码,这个测试也通过了,并符合要求。
您可以执行更多测试,开发者执行的测试级别通常取决于多个变量。这可以包括新应用程序对现有应用程序的影响,以及是否由单独的测试团队执行测试。我们将在我们的示例用户访问应用程序中完成测试。
在测试您的应用程序时,选择适合您需求的测试级别,并记住如果认为边缘情况可能会导致测试失败,请测试边缘情况。
部署
一旦完成所有测试,我们就可以将我们的自定义应用程序部署到其他实例。一旦完成以下三个主要方式之一,我们就可以部署我们的自定义应用程序:
-
将应用程序发布到更新集
-
发布到应用程序存储库
-
发布到 ServiceNow 商店
让我们看看如何使用每种方法进行部署。
发布到更新集
我们将要查看的第一种方法是如何将自定义应用程序发布到更新集。这是一个非常有用的方法,因为它允许将更新集作为检索到的更新集发送到其他实例,或者甚至在任何 ServiceNow 实例上导出和导入。这使得这种部署方法在某种程度上是最灵活的。
要将自定义应用程序发布到更新集,我们首先需要导航到应用程序记录。为此,我们可以导航到系统应用 | 应用程序,然后点击应用程序的名称。请确保不要点击编辑,因为这将打开工作室。
在相关链接菜单下,点击“发布到更新集...”选项,然后以下屏幕将显示:


在我们的示例中,我们看到的是测试应用程序名称,但您的自定义应用程序名称将在这里显示。输入版本号,并建议添加描述,这样用户就会知道应用程序的功能。我们在本章前面看到,我们可以如何将数据文件添加到我们的应用程序中作为演示数据。最后的复选框允许将演示数据包含在或排除在我们创建的更新集中。
一旦弹出表单完成,将出现一个进度条:

图 12.23:发布应用程序进度条
发布完成后,点击“完成”按钮,您将被重定向到已创建的更新集。更新集处于完整状态,因此可以立即从其他实例检索。请记住,也可以将更新集导出为 XML 格式。
现在,应用程序包含在一个完成的更新集中,它可以很容易地在实例之间移动。
发布到应用程序仓库
接下来,我们看看如何将我们的自定义应用程序发布到应用程序仓库。应用程序仓库是公司范围内的,它允许在同一公司的实例之间轻松移动新的自定义应用程序和升级。
首先,我们需要导航到应用程序记录。为此,我们可以导航到系统应用程序 | 应用程序,然后再次点击应用程序的名称。
在相关链接中,点击“发布到我的应用程序仓库”链接。按照与发布到更新集类似的方式完成流程,然后应用程序将可供与当前实例公司关联的其他所有实例使用。
我们可以通过导航到系统应用程序 | 应用程序并点击下载选项卡,在同一个公司的不同实例上安装应用程序。从这里,我们应该看到我们发送到仓库的应用程序,并显示安装选项。
一旦应用程序被安装,对创建应用程序的实例中应用程序所做的更新也将对其他实例中的特定公司可用,如果它们像应用程序一样被添加到仓库中。
发布到 ServiceNow 商店
我们将要探索的最终部署方法是发布一个应用程序到 ServiceNow 商店。ServiceNow 商店是开发者创建自定义应用程序的一种方式,这些应用程序随后通过 ServiceNow 商店出售给那些在其实例中寻找该功能的公司。应用程序可以一次性收费,按月订阅,或者通过与发展公司进行咨询来销售。
要将应用程序发布到 ServiceNow 商店,公司必须是 ServiceNow 的技术合作伙伴,因此此选项在所有实例中可能不可用。
要开始此过程,我们再次导航到“系统应用程序”|“应用程序”,然后点击应用程序的名称。一旦打开应用程序记录,我们点击相关链接“发布到 ServiceNow 商店”。这将把应用程序中的代码打包到一个可以编辑的预发布区域。
将应用程序发布到 ServiceNow 商店是一个相当复杂的过程,需要 ServiceNow 审查应用程序本身以及与该应用程序相关的文档。我们已经探讨了如何开始此过程,但一旦开始,ServiceNow 将指导您从开发人员到将应用程序带入 ServiceNow 商店的要求。
示例应用程序
我们的用户访问示例应用程序现在已完成开发并经过测试。我们现在需要将应用程序部署到其他 ServiceNow 实例。
对于我们的示例,我们将选择将应用程序发布到更新集的方法,以便我们可以将此应用程序带到任何 ServiceNow 实例。正如我们在本节中较早了解的那样,我们需要打开用户访问应用程序并点击相关链接“发布到更新集...”以开始。
在出现的窗口中,我们按照以下所示填写表格:

图 12.24:用户访问应用程序的发布到更新集弹出窗口
我们的用户访问应用程序没有演示数据,因此我们可以取消选中该框。同时,我们还添加了描述,以便安装应用程序的管理员了解该应用程序可以提供哪些功能。
点击发布按钮后,一旦过程完成,我们就可以看到新的更新集。当创建时,此更新集已设置为完成状态。
我们可以在此处看到为我们的应用程序创建的更新集:

图 12.25:已发布自定义应用程序用户访问的更新集
我们的应用程序更新集现在可以通过相关链接导出为 XML,这将允许将应用程序导入到任何其他 ServiceNow 实例。
要将更新集导入到另一个实例,系统管理员可以简单地通过右键单击列表视图的标题栏并选择“导入 XML”来导入 XML。然后,更新集将可供预览和提交。
还可以设置或使用现有的更新源,将此完成的更新集拉入另一个 ServiceNow 实例。
通过我们的用户访问应用程序示例,我们了解了自定义应用程序开发的各个阶段以及如何通过每个阶段。ServiceNow 是一个如此开放的平台,可以创建的自定义应用程序几乎无限。
摘要
在本章的最后一章,我们探讨了如何构建一个自定义应用程序。我们讨论了如何创建一个新的自定义应用程序以及可用的不同起点。我们深入研究了自定义应用程序的端到端开发,包括使用工作室。最后,我们探讨了在开发完成后如何测试和部署自定义应用程序。
通过本章,我们还看到了如何将一个示例用户访问应用程序带过创建自定义应用程序的所有阶段。从创建到开发,再到测试,我们最终创建了一个包含我们的应用程序的更新集,以便在其他 ServiceNow 实例上部署。
构建自定义应用程序是一个极好的学习经历,也是练习本书中涵盖的技术的一个绝佳方式。我希望你们能享受创造性和创建自己的一些自定义应用程序。


浙公网安备 33010602011771号