以编程方式修改 Remote Config

本文档介绍如何以编程方式读取和修改一组 JSON 格式的参数和条件(称为 Remote Config 模板)。这样,您就可以在后端更改模板,而客户端应用可以使用客户端库获取更改。

借助此指南中所述的 Remote Config REST APIAdmin SDK,您可以直接将 Remote Config 更改集成到您自己的流程中,而不必在 Firebase 控制台中管理模板。例如,使用 Remote Config 后端 API,您可以:

  • 安排 Remote Config 更新。您可以结合使用 API 调用与 Cron 作业来定期更改 Remote Config 的值。
  • 批量导入配置值,从而高效地从您自己的专有系统过渡到 Firebase Remote Config。
  • 将 Remote Config 与 Cloud Functions for Firebase 搭配使用,根据服务器端发生的事件更改应用中的值。例如,您可以使用 Remote Config 在应用中推广某项新功能,然后在检测到有足够多的用户与这项新功能进行交互后,自动停止推广。

    展示 Remote Config 后端与自定义工具和服务器进行交互的示意图

本指南的以下部分介绍了使用 Remote Config 后端 API 可以执行的操作。如需参考通过 REST API 执行这些任务的代码,请参阅以下示例应用:

使用 Firebase Admin SDK 修改 Remote Config

Admin SDK 是一组服务器库,让您可以从特权环境与 Firebase 进行交互。除了对 Remote Config 执行更新之外,Admin SDK 还支持生成和验证 Firebase 身份验证令牌,在 Realtime Database 中读取和写入数据等。 如需详细了解 Admin SDK 的使用要求和设置,请参阅将 Firebase Admin SDK 添加到您的服务器

在典型的 Remote Config 流程中,您可能需要获取当前模板,修改一些参数或参数组和条件,验证该模板,然后发布该模板。在执行这些 API 调用之前,您必须先对 SDK 发出的请求进行授权。

初始化 SDK 并对 API 请求授权

如果在初始化 Admin SDK 时未提供任何参数,该 SDK 将使用 Google 应用默认凭据并从 FIREBASE_CONFIG 环境变量中读取选项。如果 FIREBASE_CONFIG 变量的内容以 { 开头,系统会将其解析为 JSON 对象。如果不是以该字符开头,则 SDK 会假定该字符串是包含选项的 JSON 文件的名称。

例如:

Node.js

const admin = require('firebase-admin');
admin.initializeApp();

Java

FileInputStream serviceAccount = new FileInputStream("service-account.json");
FirebaseOptions options = FirebaseOptions.builder()
        .setCredentials(GoogleCredentials.fromStream(serviceAccount))
        .build();
FirebaseApp.initializeApp(options);

获取当前的 Remote Config 模板

使用 Remote Config 模板时,请注意以下几点:这些模板受版本控制,每个版本的有效期均为 90 天(从创建之日起到使用更新版本将其替换为止),并且存储的版本总数不能超过 300 个。如需了解详情,请参阅模板和版本控制

您可以使用后端 API 获取 JSON 格式 Remote Config 模板的当前活跃版本。

专门作为 A/B Testing 实验中的变体而创建的参数及参数值不包含在导出的模板中。

如需获取模板,请使用以下代码:

Node.js

function getTemplate() {
  var config = admin.remoteConfig();
  config.getTemplate()
      .then(function (template) {
        console.log('ETag from server: ' + template.etag);
        var templateStr = JSON.stringify(template);
        fs.writeFileSync('config.json', templateStr);
      })
      .catch(function (err) {
        console.error('Unable to get template');
        console.error(err);
      });
}

Java

Template template = FirebaseRemoteConfig.getInstance().getTemplateAsync().get();
// See the ETag of the fetched template.
System.out.println("ETag from server: " + template.getETag());

修改 Remote Config 参数

您可以通过编程方式修改和添加 Remote Config 参数和参数组。例如,向名为“new_menu”的现有参数组添加一个参数,以控制季节性信息的显示:

Node.js

function addParameterToGroup(template) {
  template.parameterGroups['new_menu'].parameters['spring_season'] = {
    defaultValue: {
      useInAppDefault: true
    },
    description: 'spring season menu visibility.',
  };
}

Java

template.getParameterGroups().get("new_menu").getParameters()
        .put("spring_season", new Parameter()
                .setDefaultValue(ParameterValue.inAppDefault())
                .setDescription("spring season menu visibility.")
        );

您可以利用该 API 创建新参数和参数组,或修改默认值、条件值和说明。 在任何情况下,您都必须在修改后明确发布模板。

修改 Remote Config 条件

您可以通过编程方式修改和添加 Remote Config 条件和条件值。例如,如需添加新条件,请使用以下代码:

Node.js

function addNewCondition(template) {
  template.conditions.push({
    name: 'android_en',
    expression: 'device.os == \'android\' && device.country in [\'us\', \'uk\']',
    tagColor: 'BLUE',
  });
}

Java

template.getConditions().add(new Condition("android_en",
        "device.os == 'android' && device.country in ['us', 'uk']", TagColor.BLUE));

在任何情况下,您都必须在修改后明确发布模板。

Remote Config 后端 API 提供了多个条件和比较运算符,可供您更改应用的行为和外观。如需详细了解条件以及这些条件支持的运算符,请参阅条件表达式参考文档

验证 Remote Config 模板

您也可以选择在发布更新之前对其进行验证,如下所示:

Node.js

function validateTemplate(template) {
  admin.remoteConfig().validateTemplate(template)
      .then(function (validatedTemplate) {
        // The template is valid and safe to use.
        console.log('Template was valid and safe to use');
      })
      .catch(function (err) {
        console.error('Template is invalid and cannot be published');
        console.error(err);
      });
}

Java

try {
  Template validatedTemplate = FirebaseRemoteConfig.getInstance()
          .validateTemplateAsync(template).get();
  System.out.println("Template was valid and safe to use");
} catch (ExecutionException e) {
  if (e.getCause() instanceof FirebaseRemoteConfigException) {
    FirebaseRemoteConfigException rcError = (FirebaseRemoteConfigException) e.getCause();
    System.out.println("Template is invalid and cannot be published");
    System.out.println(rcError.getMessage());
  }
}

此验证过程会检查错误,例如重复的参数和条件键、无效的条件名称、不存在的条件或格式错误的 ETag。例如,如果请求包含的键数超过允许的数量(2000 个),则会返回错误消息 Param count too large

发布 Remote Config 模板

检索到模板并按您期望的更新对其进行修改后,就可以发布该模板了。按照本部分中的说明发布模板后,系统即会将整个现有配置模板替换为更新的文件,为新的活跃模板分配一个版本号,该版本号会比其替换的模板大一个数字。

如有必要,可以使用 REST API 回滚到先前版本。 为减少更新出错的风险,您可以在发布之前先进行验证

下载的模板中包含 Remote Config 个性化设置和条件,因此在尝试将模板发布到其他项目中时,务必注意以下限制:

  • 无法在不同的项目之间导入个性化设置。

    例如,如果您在项目中启用了个性化设置,之后下载并修改了一个模板,那么您可以将该模板发布到同一项目中,但不能将其发布到其他项目中,除非您删除该模板中的个性化设置。

  • 您可以在不同的项目之间导入条件,但请注意,在发布模板之前,任何特定的条件值(如应用 ID 或受众群体)必须已存在于目标项目中。

    例如,如果您的 Remote Config 参数使用了指定平台值 iOS 的条件,那么您可以将模板发布到其他项目,因为所有项目的平台值都是相同的。但是,如果其中包含的条件依赖于目标项目中不存在的特定条件值(如某个特定应用 ID 或某一特定受众群体),则验证将失败。

  • 如果您计划发布的模板包含依赖于 Google Analytics(分析)的条件,则必须在目标项目中启用 Google Analytics(分析)。

Node.js

function publishTemplate() {
  var config = admin.remoteConfig();
  var template = config.createTemplateFromJSON(
      fs.readFileSync('config.json', 'UTF8'));
  config.publishTemplate(template)
      .then(function (updatedTemplate) {
        console.log('Template has been published');
        console.log('ETag from server: ' + updatedTemplate.etag);
      })
      .catch(function (err) {
        console.error('Unable to publish template.');
        console.error(err);
      });
}

Java

try {
  Template publishedTemplate = FirebaseRemoteConfig.getInstance()
          .publishTemplateAsync(template).get();
  System.out.println("Template has been published");
  // See the ETag of the published template.
  System.out.println("ETag from server: " + publishedTemplate.getETag());
} catch (ExecutionException e) {
  if (e.getCause() instanceof FirebaseRemoteConfigException) {
    FirebaseRemoteConfigException rcError = (FirebaseRemoteConfigException) e.getCause();
    System.out.println("Unable to publish template.");
    System.out.println(rcError.getMessage());
  }
}

使用 REST API 修改 Remote Config

本部分介绍 https://firebaseremoteconfig.googleapis.com 中 Remote Config REST API 的主要功能。 如需了解完整详情,请参阅 API 参考文档

获取访问令牌以对 API 请求进行身份验证和授权

Firebase 项目支持 Google 服务账号,您可以使用这些账号从应用服务器或受信任环境调用 Firebase 服务器 API。如果您是在本地编写代码,或是在本地部署您的应用,则可以使用通过此服务账号获取的凭据来对服务器请求进行授权。

如需对服务账号进行身份验证并授予其访问 Firebase 服务的权限,您必须生成 JSON 格式的私钥文件。

如需为您的服务账号生成私钥文件,请执行以下操作:

  1. 在 Firebase 控制台中,打开设置 > 服务账号

  2. 点击生成新的私钥,然后点击生成密钥进行确认。

  3. 妥善存储包含密钥的 JSON 文件。

通过服务账号进行授权时,有两种方式可为您的应用提供凭据。您可以设置 GOOGLE_APPLICATION_CREDENTIALS 环境变量,也可以在代码中明确传递服务账号密钥的路径。第一种方式更为安全,因此强烈推荐使用此方式。

如需设置该环境变量,请执行以下操作

将环境变量 GOOGLE_APPLICATION_CREDENTIALS 设置为包含服务账号密钥的 JSON 文件的路径:此变量仅适用于当前的 Shell 会话,因此请在开始新的会话时重新设置该变量。

Linux 或 macOS

export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"

Windows

使用 PowerShell:

$env:GOOGLE_APPLICATION_CREDENTIALS="C:\Users\username\Downloads\service-account-file.json"

完成上述步骤后,应用默认凭据 (ADC) 能够隐式确定您的凭据,如此您便能在非 Google 环境中测试或运行时使用服务账号凭据。

将您的 Firebase 凭据与适用于您的偏好语言的 Google API 客户端库结合使用,以检索短期有效的 OAuth 2.0 访问令牌:

node.js

 function getAccessToken() {
  return admin.credential.applicationDefault().getAccessToken()
      .then(accessToken => {
        return accessToken.access_token;
      })
      .catch(err => {
        console.error('Unable to get access token');
        console.error(err);
      });
}

在此示例中,Google API 客户端库使用 JSON Web 令牌 (JWT) 对请求进行身份验证。有关详情,请参阅 JSON Web 令牌

Python

def _get_access_token():
  """Retrieve a valid access token that can be used to authorize requests.

  :return: Access token.
  """
  credentials = ServiceAccountCredentials.from_json_keyfile_name(
      'service-account.json', SCOPES)
  access_token_info = credentials.get_access_token()
  return access_token_info.access_token

Java

public static String getAccessToken() throws IOException {
  GoogleCredentials googleCredentials = GoogleCredentials
          .fromStream(new FileInputStream("service-account.json"))
          .createScoped(Arrays.asList(SCOPES));
  googleCredentials.refreshAccessToken();
  return googleCredentials.getAccessToken().getTokenValue();
}

在您的访问令牌到期后,系统会自动调用令牌刷新方法以检索更新的访问令牌。

如需授予访问 Remote Config 的权限,应请求 https://www.googleapis.com/auth/firebase.remoteconfig 范围。

修改 Remote Config 模板

使用 Remote Config 模板时,请注意以下几点:这些模板受版本控制,每个版本的有效期均为 90 天(从创建之日起到使用更新版本将其替换为止),并且存储的版本总数不能超过 300 个。如需了解详情,请参阅模板和版本控制

获取当前的 Remote Config 模板

您可以使用后端 API 获取 JSON 格式 Remote Config 模板的当前活跃版本。

专门作为 A/B Testing 实验中的变体而创建的参数及参数值不包含在导出的模板中。

使用以下命令:

cURL

curl --compressed -D headers -H "Authorization: Bearer token" -X GET https://firebaseremoteconfig.googleapis.com/v1/projects/my-project-id/remoteConfig -o filename

此命令会将 JSON 有效载荷输出到一个文件中,并将标头(包括 Etag)输出到一个单独的文件中。

原始 HTTP 请求

Host: firebaseremoteconfig.googleapis.com

GET /v1/projects/my-project-id/remoteConfig HTTP/1.1
Authorization: Bearer token
Accept-Encoding: gzip

此 API 调用会返回以下 JSON 以及一个单独的标头,其中包含用于后续请求的 ETag

验证 Remote Config 模板

您也可以选择在发布更新前先对其进行验证。 将网址参数 ?validate_only=true 附加到您的发布请求,来验证模板更新。 如果响应中包含状态代码 200 以及后缀为 -0 的更新后的 ETag,则表示您的更新已成功通过验证。如果响应中的状态代码不是 200,则表示 JSON 数据包含错误,您必须在发布前进行更正。

更新 Remote Config 模板

检索模板并按您期望的更新修改 JSON 内容后,就可以发布该模板了。按照本部分中的说明发布模板后,系统即会将整个现有配置模板替换为更新的文件,为新的活跃模板分配一个版本号,该版本号会比其替换的模板大一个数字。

如有必要,可以使用 REST API 回滚到先前版本。 为减少更新出错的风险,您可以在发布之前先进行验证

下载的模板中包含 Remote Config 个性化设置和条件,因此在尝试将模板发布到其他项目中时,务必注意以下限制:

  • 无法在不同的项目之间导入个性化设置。

    例如,如果您在项目中启用了个性化设置,之后下载并修改了一个模板,那么您可以将该模板发布到同一项目中,但不能将其发布到其他项目中,除非您删除该模板中的个性化设置。

  • 您可以在不同的项目之间导入条件,但请注意,在发布模板之前,任何特定的条件值(如应用 ID 或受众群体)必须已存在于目标项目中。

    例如,如果您的 Remote Config 参数使用了指定平台值 iOS 的条件,那么您可以将模板发布到其他项目,因为所有项目的平台值都是相同的。但是,如果其中包含的条件依赖于目标项目中不存在的特定条件值(如某个特定应用 ID 或某一特定受众群体),则验证将失败。

  • 如果您计划发布的模板包含依赖于 Google Analytics(分析)的条件,则必须在目标项目中启用 Google Analytics(分析)。

cURL

curl --compressed -H "Content-Type: application/json; UTF8" -H "If-Match: last-returned-etag" -H "Authorization: Bearer token" -X PUT https://firebaseremoteconfig.googleapis.com/v1/projects/my-project-id/remoteConfig -d @filename

对于 curl 命令,您可以使用“@”字符后跟文件名来指定相应的内容。

原始 HTTP 请求

Host: firebaseremoteconfig.googleapis.com
PUT /v1/projects/my-project-id/remoteConfig HTTP/1.1
Content-Length: size
Content-Type: application/json; UTF8
Authorization: Bearer token
If-Match: expected ETag
Accept-Encoding: gzip
JSON_HERE

由于这是一个写入请求,因此该命令将修改 ETag,并将在下一个 PUT 命令的响应标头中提供更新后的 ETag。

修改 Remote Config 条件

您可以通过编程方式修改 Remote Config 条件和条件值。使用 REST API 时,必须直接编辑模板以修改条件,然后才能发布模板。

{
  "conditions": [{
    "name": "android_english",
    "expression": "device.os == 'android' && device.country in ['us', 'uk']",
    "tagColor": "BLUE"
  }, {
    "name": "tenPercent",
    "expression": "percent <= 10",
    "tagColor": "BROWN"
  }],
  "parameters": {
    "welcome_message": {
      "defaultValue": {
        "value": "Welcome to this sample app"
      },
      "conditionalValues": {
        "tenPercent": {
          "value": "Welcome to this new sample app"
        }
      },
      "description": "The sample app's welcome message"
    },
    "welcome_message_caps": {
      "defaultValue": {
        "value": "false"
      },
      "conditionalValues": {
        "android_english": {
          "value": "true"
        }
      },
      "description": "Whether the welcome message should be displayed in all capital letters."
    }
  }
}

上述修改首先定义了一组条件,然后为每个参数定义了默认值和基于条件的参数值(“条件值”)。它还为每个元素添加了说明(可选);像代码注释一样,这些是供开发者使用的,并不显示在应用中。另外,为便于版本控制,还提供了一个 ETag

Remote Config 后端 API 提供了多个条件和比较运算符,可供您更改应用的行为和外观。如需详细了解条件以及这些条件支持的运算符,请参阅条件表达式参考文档

HTTP 错误代码

状态代码 含义
200 已成功更新
400 发生验证错误。例如,如果某个请求包含的键数超过允许的数量(2000 个),则会返回 400(请求错误)错误,并显示错误消息 Param count too large。 另外,在以下两种情况下,也可能会出现此 HTTPS 状态代码:
  • 自您上次检索 ETag 值后该组值和条件已经过更新,导致发生版本不匹配错误。若要解决此错误,您应该使用 GET 命令获取最新的模板和 ETag 值,更新模板后,使用该模板和最新 ETag 值进行提交。
  • 在未指定 If-Match 标头的情况下执行了 PUT 命令(更新 Remote Config 模板请求)。
401 发生授权错误(未提供访问令牌;或者 Firebase Remote Config REST API 尚未添加到您在 Cloud 开发者控制台中的项目)
403 发生身份验证错误(提供的访问令牌有误)
500 发生内部错误。如果发生此错误,请提交 Firebase 支持服务工单

如果看到状态代码 200,则意味着 Remote Config 模板(项目的参数、值和条件)已更新,现已可用于使用此项目的应用。如果看到其他状态代码,则意味着以前存在的 Remote Config 模板仍然有效。

在您提交对模板的更新后,请转到 Firebase 控制台,以验证您所做的更改是否按预期显示。这一步至关重要,因为条件的排序会影响它们的求值方式(第一个求值为 true 的条件会生效)。

ETag 用法和强制更新

Remote Config REST API 使用了实体标记 (ETag) 来避免竞态条件和对资源的重叠更新。如需详细了解 ETag,请参阅 ETag - HTTP

针对 REST API,Google 建议您缓存最新 GET 命令提供的 ETag,并在发出 PUT 命令时在 If-Match 请求标头中使用该 ETag 值。如果您的 PUT 命令导致出现 HTTPS 状态代码 409,您应该用新的 GET 命令来获取新的 ETag 和模板,供下一个 PUT 命令使用。

通过强制 Remote Config 模板按如下方式进行更新,您可以不使用 ETag 及其提供的保护:If-Match: *。但是,我们不建议使用此方法,因为如果有多个客户端正在更新 Remote Config 模板,此方法可能会导致对 Remote Config 模板的更新丢失。如果多个客户端都在使用 API,或者来自 API 客户端和 Firebase 控制台用户的更新存在冲突,都可能造成这种冲突。

如需管理 Remote Config 模板版本的指导,请参阅 Remote Config 模板和版本控制