如何构建服务器代码

服务器端代码植入简介中,您大致了解了跟踪代码管理器中的服务器端代码植入。您已学习了客户端的定义及其用途:客户端接收来自用户设备的事件数据,并对其进行调整,供容器的其余部分使用。本文将介绍如何使用服务器端代码处理这些数据。

在服务器容器中,代码从客户端接收传入的事件数据,对其进行转换,然后重新发送出去以供收集和分析。代码可以将数据发送到任何目的地。只要能接受 HTTP 请求,那么目的地也可以接受来自服务器容器的数据。

服务器容器有三种内置代码,它们无需进行自定义配置即可使用:

  • Google Analytics(分析)4
  • Google Analytics(分析):Universal Analytics
  • HTTP 请求

如果您要将数据发送到 Google Analytics(分析)以外的位置,或者需要 HTTP 请求代码提供的功能之外的更多功能,则需要使用其他代码。您可以在社区模板库中找到其他代码,也可以自行编写。本教程将介绍自行编写服务器容器代码的基础知识。

目标

  • 了解应使用哪些 API 来读取事件数据、发送 HTTP 请求以及在浏览器中设置 Cookie。
  • 了解设计代码配置选项的最佳做法。
  • 了解用户指定的数据与自动收集的数据之间的差异,以及这一差异的重要意义。
  • 了解代码在服务器容器中的作用。知道代码应该和不应该执行的操作。
  • 了解在何时可以考虑向社区模板库提交代码模板。

前提条件

Baz Analytics 代码

在本教程中,您将创建代码,以便将衡量数据发送到名为 Baz Analytics 的服务。

Baz Analytics 是一项简单的假设分析服务,可以通过 HTTP GET 请求将数据提取到 https://example.com/baz_analytics。它包含以下参数:

参数 示例 说明
id BA-1234 Baz Analytics 账号的 ID。
en click 事件名称。
l https://www.google.com/search?q=sgtm 发生事件的网页网址。
u 2384294892 执行相应操作的用户的 ID。用于将多项操作与单个用户联系起来。

代码配置

首先需要创建代码模板。前往容器的模板部分,然后点击代码模板部分的新建。为代码添加名称和说明。

接下来,转到模板编辑器的字段部分,为代码添加不同的配置选项。显而易见,下一个问题是:您需要哪些选项?您可以通过以下三种方式构建代码:

  1. 总体配置:向每个参数添加配置字段。要求用户明确设置所有内容。
  2. 无配置:没有配置代码的选项。所有数据都直接从事件中获取。
  3. 部分配置:为部分参数设置字段。

为每个参数设置字段的方法非常灵活,用户可以全面控制自己的代码配置。但在实际使用中,这通常会导致许多重复工作。尤其需要注意的是,Baz Analytics l 参数(包含网页网址)等内容明确且通用。若在每次配置代码时都会输入相同且不变的数据项,则最好让计算机来完成这样的操作。

或许,可以使用仅从事件中获取数据的代码。这是用户可以配置的最简单可行的代码,因为用户实际上无需执行任何操作,然而这也是最严格和脆弱的方式。即使用户有需要,也无法改变代码的行为。例如,他们可能会在网站上和 Google Analytics(分析)中调用事件 purchase,但 Baz Analytics 会调用 buy。或者,或许代码对于传入事件数据的结构所做假设与现实情况并不相符。不管是哪种情况,用户都会遇到问题。

与许多情况一样,解决办法就是平衡两种极端情况。对于某些数据而言,系统可以始终从事件中获取。除此之外,其他数据则应由用户配置。如何区分这些数据?要回答这个问题,我们需要进一步探究进入容器的数据。

数据来自哪里?

从 Google Analytics(分析)4 代码进入服务器容器的数据大致可以分为两类:用户指定的数据和自动收集的数据。

用户指定的数据是用户输入到 gtag.js event 命令中的所有内容。例如,如下所示的命令:

gtag('event', 'search', {
  search_term: 'beets',
});

会在服务器容器中生成以下参数:

{
  event_name: 'search',
  search_term: 'beets',
}

看上去非常简单,但从代码的角度来看,很难处理。由于该数据是用户输入的,因此可以是任何内容。如上所述,或许用户只是发送了推荐事件和参数,但系统并不要求他们这么做。除了 event_name 参数的位置(但不是值!)这一不容忽视的例外情况,用户数据的形式或结构都无法得到保证。

所幸的是,用户输入的数据并不是容器会接收的唯一内容。另外,浏览器中的 Google Analytics(分析)4 代码也会自动收集大量数据。这些数据包括:

  • ip_override
  • language
  • page_location
  • page_referrer
  • page_title
  • screen_resolution
  • user_agent

此外,如果服务器请求来自网络浏览器,或许还可以通过 getCookieValue API 获取浏览器 Cookie 数据。

这些共同构成了上述自动收集的数据。一般来说,包括通用数据和语义清晰的数据。当浏览器中的 GA4 代码发出请求时,该数据将保持可用并始终采用相同格式。如需详细了解这些参数,请查看事件参考文档

在确定哪些数据应由用户配置,哪些数据应在代码中指定时,这种分类信息是一款十分有用的工具。自动收集的数据非常安全,可以直接从事件中读取。其他所有数据均应由用户配置。

有鉴于此,不妨再次查看 Baz Analytics 代码的参数。

  • 衡量 ID,id:因为系统不会自动收集这个 ID,所以它是配置代码时应由用户输入的值的明确示例。
  • 事件名称,en:如上所述,事件名称始终可以直接从 event_name 参数中获取。但是,由于其值是由用户定义的,因此不妨根据需要提供覆盖名称的功能。
  • 网页网址,l:此值可取自 page_location 参数,由 Google Analytics(分析)GA4 浏览器代码自动针对每个事件收集。因此,您不应要求用户手动输入值。
  • 用户 ID,u:在 Baz Analytics 服务器代码中,u 参数既非用户指定,也非由网页上的代码自动收集。而是存储在浏览器 Cookie 中,用于在用户多次访问网站时识别用户。如以下代码植入所示,它是使用 setCookie API 设置 Cookie 的 Baz Analytics 服务器代码。这意味着,Baz Analytics 代码是了解 Cookie 存储位置和方式的唯一途径。与 l 类似,u 参数也应自动收集。

设置好代码配置后,其配置应如下所示:

Baz Analytics 代码的代码配置快照。

代码实现

至此,代码已妥善配置,接下来就可以在沙盒化 JavaScript 中实现其行为了。

代码需要执行以下四项操作:

  1. 从代码配置中获取事件名称。
  2. 从事件的 page_location 属性中获取网页网址。
  3. 计算用户 ID。代码会在名为 _bauid 的 Cookie 中查找用户 ID。如果该 Cookie 不存在,则代码将计算新值并将其存储起来,以备后续请求使用。
  4. 构建网址,并向 Baz Analytics 收集服务器发出请求。

不妨花些时间思考一下代码应如何从整体上适应容器。不同的容器组件具有不同作用,因此代码无法或不应用于某些目的。您的代码:

  • 不应检查事件来确定它是否应该运行。这是触发器的用途。
  • 不应使用 runContainer API 运行容器。这是客户端的工作。
  • 除了 Cookie 不容忽视的例外情况外,还不应尝试直接与请求或响应进行交互。这也是客户端的工作。

若编写执行此类操作的代码模板,将导致代码的用户行为发生错乱。例如,向传入请求发送响应的代码会阻止客户端执行相同的操作。这会扰乱用户对容器应有行为的预期。

考虑了以上所有方面后,以下是在沙盒化 JavaScript 中实现的代码(带注释)。

const encodeUriComponent = require('encodeUriComponent');
const generateRandom = require('generateRandom');
const getCookieValues = require('getCookieValues');
const getEventData = require('getEventData');
const logToConsole = require('logToConsole');
const makeString = require('makeString');
const sendHttpGet = require('sendHttpGet');
const setCookie = require('setCookie');

const USER_ID_COOKIE = '_bauid';
const MAX_USER_ID = 1000000000;

// The event name is taken from either the tag's configuration or from the
// event. Configuration data comes into the sandboxed code as a predefined
// variable called 'data'.
const eventName = data.eventName || getEventData('event_name');

// page_location is automatically collected by the Google Analytics 4 tag.
// Therefore, it's safe to take it directly from event data rather than require
// the user to specify it. Use the getEventData API to retrieve a single data
// point from the event. There's also a getAllEventData API that returns the
// entire event.
const pageLocation = getEventData('page_location');
const userId = getUserId();

const url = 'https://www.example.com/baz_analytics?' +
    'id=' + encodeUriComponent(data.measurementId) +
    'en=' + encodeUriComponent(eventName) +
    (pageLocation ? 'l=' + encodeUriComponent(pageLocation) : '') +
    'u=' + userId;

// The sendHttpGet API takes a URL and returns a promise that resolves with the
// result once the request completes. You must call data.gtmOnSuccess() or
// data.gtmOnFailure() so that the container knows when the tag has finished
// executing.
sendHttpGet(url).then((result) => {
  if (result.statusCode >= 200 && result.statusCode < 300) {
    data.gtmOnSuccess();
  } else {
    data.gtmOnFailure();
  }
});

// The user ID is taken from a cookie, if present. If it's not present, a new ID
// is randomly generated and stored for later use.
//
// Generally speaking, tags should not interact directly with the request or
// response. This prevents different tags from conflicting with each other.
// Cookies, however, are an exception. Tags are the only container entities that
// know which cookies they need to read or write. Therefore, it's okay for tags
// to interact with them directly.
function getUserId() {
  const userId = getCookieValues(USER_ID_COOKIE)[0] || generateRandom(0, MAX_USER_ID);
  // The setCookie API adds a value to the 'cookie' header on the response.
  setCookie(USER_ID_COOKIE, makeString(userId), {
    'max-age': 3600 * 24 * 365 * 2,
    domain: 'auto',
    path: '/',
    httpOnly: true,
    secure: true,
  });

  return userId;
}

这样一来,就可以实现代码了。要使用该代码,您首先要正确设置其 API 权限。前往模板编辑器的权限标签页,然后指定以下权限:

  • 读取 Cookie 值:_bauid
  • 读取事件数据:event_namepage_location
  • 发送 HTTP 请求:https://www.example.com/*
  • 设置 Cookie:_bauid

您还应该为代码编写测试。如需详细了解模板测试,请参阅模板开发者指南的测试部分。

最后,别忘了至少使用一次运行代码按钮来尝试运行代码,这样可以避免服务器上出现许多低级错误。

目前您已完成了包括创建、测试和部署新代码在内的所有工作,我们建议您把这些信息分享给大家。如果您认为新代码可能会对他人有帮助,不妨考虑将其提交到社区模板库。

总结

在本教程中,您学习了为服务器容器编写代码的基础知识。您学习了:

  • 哪些 API 可用于读取事件数据、发送 HTTP 请求以及在浏览器中设置 Cookie。
  • 为代码设计配置选项的最佳做法。
  • 用户指定的数据与自动收集的数据之间的差异,以及这一差异的重要意义。
  • 代码在容器中的作用及其应该和不应该执行的操作。
  • 将代码模板提交到社区模板库的时机和方式。