JavaScript 对象模型

本主题讨论了 Apigee JavaScript 对象模型。如果您打算使用 JavaScript 政策将自定义 JavaScript 添加到 API 代理,则必须了解此模型。

JavaScript 对象模型简介

JavaScript 对象模型定义具有关联属性的对象,这些对象可供在 Apigee 代理流中执行的 JavaScript 代码使用。您可以使用 JavaScript 政策将此自定义代码附加到 API 代理流。

此模型定义的对象的范围在 API 代理流内,这意味着某些对象和属性仅在流程的特定点可用。当 JavaScript 执行时,系统会为执行创建一个范围。在该范围内创建以下对象引用:contextrequestresponsecrypto。对象模型还包含一个可用于调试的 print 函数。

上下文对象

context 对象具有全局范围。它在 API 代理流中的任何位置都可用。它有四个子对象:proxyRequestproxyResponsetargetRequesttargetResponse。这些子对象仅限于环境请求和响应,包括代理请求和响应以及目标请求和响应。例如,如果 JavaScript 政策在流的代理端点部分执行,则 context.proxyRequestcontext.proxyResponse 对象都在范围内。如果 JavaScript 在目标流中运行,则 context.targetRequestcontext.targetResponse 对象在范围内。

context 对象还具有属性和方法,本主题对此进行了详细说明。例如,以下 JavaScript 代码示例使用 context.flow 属性,并对 context 调用 get/setVariable() 方法。

if (context.flow=="PROXY_REQ_FLOW") {
     var username = context.getVariable("request.formparam.user");
     context.setVariable("USER.name", username);
}

这些方法直接与流变量进行交互。context.flow 属性值是当前的流范围。在代理请求流中,它被设置为常量 PROXY_REQ_FLOW。如果目标响应流中,它被设置为 TARGET_RESP_FLOW。此常量对于执行特定于范围的代码十分方便。您可以通过 getter 获取流变量,并通过 setter 设置流变量。这些变量通常在代理流中可用,并且可供其他政策使用。

如需了解详情和查看示例,请参阅上下文对象参考

加密对象

加密对象为 JavaScript 对象模型添加了基本的高性能加密支持。如需了解详情和查看示例,请参阅加密对象参考

请求和响应对象

requestresponse 对象是环境请求和响应的“简写”,可以是代理请求和响应,也可以是目标请求和响应。这些变量引用的对象取决于执行 JavaScript 政策的上下文。如果 JavaScript 在代理端点流中运行,则请求和响应变量引用的是 context.proxyRequestcontext.proxyResponse。如果 JavaScript 在目标流中运行,则变量引用的是 context.targetRequestcontext.targetResponse

print() 函数

JavaScript 对象模型包含一个 print() 函数,您可以使用该函数将调试信息输出到 Apigee Trace 工具。请参阅使用 JavaScript print() 语句进行调试

加密对象参考

加密对象可让您在 JavaScript 中执行基本加密哈希函数。

加密对象具有全局范围。它在 API 代理流中的任何位置都可用。加密支持使用以下哈希对象:

  • SHA-1
  • SHA256
  • SHA512
  • MD5

使用 SHA-1 对象

您可以创建和更新 SHA-1 对象,并将它们转换为十六进制和 base64 值。

创建新的 SHA-1 对象

var _sha1 = crypto.getSHA1();

更新 SHA-1 对象

语法

_sha1.update(value);

参数

  • value -(字符串)任何字符串值。

示例

更新 SHA-1 对象:

_sha1.update("salt_value");

_sha1.update("some text");

以十六进制字符串的形式返回 SHA-1 对象

var _hashed_token = _sha1.digest();

以 base64 字符串的形式返回 SHA-1 对象

var _hashed_token = _sha1.digest64();

使用 SHA-256 对象

您可以创建和更新 SHA-256 对象,并将它们转换为十六进制和 base64 值。

创建新的 SHA-256 对象

var _sha256 = crypto.getSHA256();

更新 SHA-256 对象

语法

_sha256.update(value);

参数

  • value -(字符串)任何字符串值。

示例

更新 SHA-256 对象:

_sha256.update("salt_value");

_sha256.update("some text");

以十六进制字符串的形式返回 SHA-256 对象

var _hashed_token = _sha256.digest();

以 base64 字符串的形式返回 SHA-256 对象

var _hashed_token = _sha256.digest64();

使用 SHA-512 对象

您可以创建和更新 SHA-512 对象,并将它们转换为十六进制和 base64 值。

创建新的 SHA-512 对象

var _sha512 = crypto.getSHA512();

更新 SHA-512 对象

语法

_sha512.update(value);

参数

  • value -(字符串)任何字符串值。

示例

更新 SHA-512 对象:

_sha512.update("salt_value");

_sha512.update("some text");

以十六进制字符串的形式返回 SHA-512 对象

var _hashed_token = _sha512.digest();

以 base64 字符串的形式返回 SHA-512 对象

var _hashed_token = _sha512.digest64();

使用 MD5 对象

您可以创建和更新 MD5 对象,并将它们转换为十六进制和 base64 值。

创建新的 MD5 对象

var _md5 = crypto.getMD5();

更新 MD5 对象

语法

_md5.update(value);

参数

  • value -(字符串)任何字符串值。

示例

更新 MD5 对象:

_md5.update("salt_value");

_md5.update("some text");

以十六进制字符串的形式返回 MD5 对象

var _hashed_token = _md5.digest();

以 base64 字符串的形式返回 MD5 对象

var _hashed_token = _md5.digest64();

加密日期/时间支持

加密对象支持日期/时间格式模式。

crypto.dateFormat()

以字符串格式返回日期。

语法

crypto.dateFormat(format, [timezone], [time])

参数

  • format -(字符串)此参数的底层实现为 java.text.SimpleDateFormat。例如:YYYY-MM-DD HH:mm:ss.SSS
  • timezone -(字符串,可选)此参数的底层实现为 java.util.TimeZone。此参数相同 默认值:UTC
  • time -(数字,可选)要设置格式的 Unix 时间戳值。默认值:当前时间

示例

获取当前时间,精确到毫秒:

var _now = crypto.dateFormat('YYYY-MM-DD HH:mm:ss.SSS');

获取太平洋时区的当前时间:

var _pst = crypto.dateFormat('YYYY-MM-DD HH:mm:ss.SSS','PST');

获取从现在开始的 10 秒的值:

var _timeNow = Number(context.getVariable('system.timestamp'));
var ten_seconds = crypto.dateFormat('YYYY-MM-DD HH:mm:ss.SSS','PST', _timeNow + 10 * 1000);

以下是更多示例。另请参阅 java.text.SimpleDateFormat 文档。

var _pst = crypto.dateFormat('M');
var _pst = crypto.dateFormat('EEE, d MMM yyyy HH:mm:ss Z');
var _pst = crypto.dateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");

使用 getHash() 获取任何支持的哈希对象

示例

var _hash1 = crypto.getHash('MD5');

var _hash2 = crypto.getHash('SHA-1');

var _hash3 = crypto.getHash('SHA-256');

var _hash4 = crypto.getHash('SHA-512');

加密示例

try {
    //get values to use with hash functions
    var salt = context.getVariable("salt") || 'SomeHardCodedSalt';
    var host = context.getVariable("request.header.Host");
    var unhashed_token = "";

    var _timeNow = Number(context.getVariable('system.timestamp'));
    var now = crypto.dateFormat('YYYY-MM-DD HH:mm:ss.SSS','PST', _timeNow);
    unhashed_token = "|" + now + "|" + host

    //generate a hash with the unhashedToken:
    var sha512 = crypto.getSHA512();
    sha512.update(salt);
    sha512.update(unhashed_token);

    //convert to base64
    var base64_token = sha512.digest64();

    // set headers
    context.setVariable("request.header.now", now);
    context.setVariable("request.header.token", base64_token);

} catch(e) {
    throw 'Error in Javascript';
}

上下文对象参考

系统会为 API 代理执行的每个请求/响应事务创建一个 context 对象。context 对象提供获取、设置和移除与每个事务相关的变量的方法。

变量定义了特定于事务的属性。context 中提供的变量的示例包括:一天中的时间、请求客户端的语言区域、请求客户端的用户代理、目标服务的网址。因此,context 可用于构建依赖于这些属性执行自定义行为的逻辑。

请参阅流变量参考ExtractVariables 政策

上下文对象摘要

下表简要介绍了上下文对象及其子对象,并列出了绑定到各个对象的属性。

名称 说明 属性
context 消息处理流水线上下文以及由 ProxyEndpoint 和 TargetEndpoint 执行的请求和响应流的封装容器。 流、会话
context.proxyRequest 表示发送到 ProxyEndpoint 的入站请求消息的对象(从请求应用到 API 代理) headers、query parameters、method、body、url
context.targetRequest 表示来自 TargetEndpoint 的出站请求消息的对象(从 API 代理到后端服务)。 headers、query parameters、method、body、url
context.targetResponse 表示入站目标响应消息的对象(从后端服务到 API 代理) headers、content、status
context.proxyResponse 表示出站代理响应消息的对象(从 API 代理到请求应用) headers、content、status
context.flow 当前流的名称。 请参阅 context.flow
context.session 名称/值对的映射,可用于在同一上下文中执行的两个不同步骤之间传递对象。例如:context.session['key'] = 123 如需详细了解何时使用以及何时不使用此对象,请参阅 context.session['hello'] = {} 与 context.setVaraible("hello", {}) 之间的区别

上下文对象方法

context.getVariable()

检索预定义变量或自定义变量的值。

语法

context.getVariable("variable-name");

示例

获取当前年份的值:

var year = context.getVariable('system.time.year');

context.setVariable()

为自定义变量或任意可写预定义变量设置值。

语法

context.setVariable("variable-name", value);

示例

设置变量的常见情况是 API 代理必须动态写入目标网址。以下 JavaScript 会获取名为 USER.name 的变量的值,将该值作为查询参数附加到网址 http://mocktarget.apigee.net?user=,然后将预定义的 target.url 设置为该值。

context.setVariable("target.url", "http://mocktarget.apigee.net/user?user="+context.getVariable("USER.name"));

context.removeVariable()

从上下文中移除变量。

语法

context.removeVariable('variable-name');

上下文对象属性

context.flow

flow 属性是一个字符串,用于标识当前的 API 代理流。此属性用于表示 JavaScript 所附加到的流。支持的值为:

  • PROXY_REQ_FLOW
  • PROXY_RESP_FLOW
  • TARGET_REQ_FLOW
  • TARGET_RESP_FLOW

每个流名称都包含 PreFlow、PostFlow 以及在 ProxyEndpoint 或 TargetEndpoint 中定义的任何条件流。

如果在多个流中执行共同的 JavaScript,此可选属性会很有用,但在其执行的各个流中,其行为可能会有所不同。为要在多个 API 代理中重复使用的 JavaScript 模块使用 flow 属性;在执行逻辑之前,代码需要检查当前的 flow。

示例

仅在 targetRequest 流上设置 HTTP 标头:

if (context.flow=="TARGET_REQ_FLOW") {
     context.targetRequest.headers['TARGET-HEADER-X']='foo';
}

仅在 proxyResponse 流上设置内容:

if (context.flow=="PROXY_RESP_FLOW") {
     context.proxyResponse.content='bar';
}

context.session

名称/值对的映射,可用于在同一消息上下文中执行的两个政策之间传递对象。

示例

在会话中设置值:

context.session['key']  = 123;

获取会话中的值:

var value = context.session['key']; // 123

上下文对象的子对象

如下所示,一个完整的 API 代理流包括四个不同的阶段,每个阶段都有一个关联的消息对象,该对象是上下文对象的子对象:

  • context.proxyRequest:从请求客户端收到的入站请求消息。
  • context.targetRequest:发送到后端服务的出站请求消息。
  • context.proxyResponse:返回到请求客户端的出站响应消息。
  • context.targetResponse:从后端服务收到的入站请求消息。

以下各部分介绍这些对象的方法和属性:

context.*请求子对象

对于在 API 代理中执行每个的 HTTP 事务,系统会创建两个请求消息对象:一个“入站”(来自客户端的请求)和一个“出站”(由 API 代理生成并提交给后端目标的请求)。

context 对象具有表示这些请求消息的子对象:context.proxyRequestcontext.targetRequest。通过这些对象,您可以访问 JavaScript 代码执行范围内的请求流内的属性。

context.*请求子对象属性

属性名称 说明
url

url 是一个方便读写的属性,包含 targetRequest 的架构、主机、端口、路径和查询参数。

请求的完整网址由以下属性组成:

  • protocol:网址的协议(例如 HTTP、HTTPS)
  • port:端口(例如 :80、:443)
  • host:网址的主机(例如 www.example.com)
  • path:URI 的路径(例如 /v1/mocktarget)

获取 url 时,网址会按以下格式返回:

protocol://host:port/path?queryParams

示例:


context.targetRequest.url = 'http://www.example.com/path?q1=1'
context.targetRequest.protocol ='https';
headers

形式为 String => List 映射的 HTTP 请求标头

示例:

对于此 HTTP 请求:


POST /v1/blogs HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer ylSkZIjbdWybfs4fUQe9BqP0LH5Z
以下 JavaScript:

context.proxyRequest.headers['Content-Type'];
context.proxyRequest.headers['Authorization'];

将返回以下值


application/json
Bearer ylSkZIjbdWybfs4fUQe9BqP0LH5Z
queryParams

形式为 String => List 映射的请求消息查询参数。

示例:


"?city=PaloAlto&city=NewYork"

可以访问如下:


context.proxyRequest.queryParams['city'];  // == 'PaloAlto'
context.proxyRequest.queryParams['city'][0]     // == 'PaloAlto'
context.proxyRequest.queryParams['city'][1];    // == 'NewYork'
context.proxyRequest.queryParams['city'].length(); // == 2
method

与请求关联的 HTTP 动词(GET、POST、PUT、DELETE、PATCH 等)

示例:

对于此请求:


POST /v1/blogs HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer ylSkZIjbdWybfs4fUQe9BqP0LH5Z

以下 JavaScript:


context.proxyRequest.method;

将返回以下值


POST
body

HTTP 请求的消息正文(载荷)。

请求正文具有以下成员:

  • context.targetRequest.body.asXML;
  • context.targetRequest.body.asJSON;
  • context.targetRequest.body.asForm;

示例:

对于 XML 正文:


<customer number='1'>
<name>Fred<name/>
<customer/>

按如下所示访问 XML 对象的元素:


var name = context.targetRequest.body.asXML.name;

要访问 XML 属性,请使用 @ 表示法。


var number = context.targetRequest.body.asXML.@number;

对于 JSON 请求正文:


{
"a":  1 ,
"b" : "2"
}

var a = context.proxyRequest.body.asJSON.a;    // == 1
var b = context.proxyRequest.body.asJSON.b;    // == 2

按如下所示读取表单参数:


"vehicle=Car&vehicle=Truck"

v0 = context.proxyRequest.body.asForm['vehicle'][0];
v1 = context.proxyRequest.body.asForm['vehicle'][1];

context.*响应子对象

对于在 API 代理中执行的每个 HTTP 事务,系统会创建两个响应消息对象:一个“入站”(后端服务的响应)和一个“出站”(将发送回客户端的响应)。

上下文对象具有代表这些响应消息的子对象:context.proxyResponsecontext.targetResponse。通过这些对象,您可以访问 JavaScript 代码执行范围内的响应流内的属性。

context.*响应对象属性

属性名称 说明
headers

形式为 String => List 映射的响应消息的 HTTP 标头。

例如:


var cookie = context.targetResponse.headers['Set-Cookie'];
status

作为属性的状态代码,带有状态消息。状态代码和状态消息均作为属性提供。

例如:


var status = context.targetResponse.status.code;   // 200
var msg = context.targetResponse.status.message;   // "OK"
content

响应消息的 HTTP 正文(载荷内容)。

响应内容包括以下成员:


context.targetResponse.content.asXML;
context.targetResponse.content.asJSON;

使用 .asXML 表示法

使用 .asXML 表示法可以方便地浏览 XML 文档。本部分介绍如何使用此表示法,以及它与 request.contentcontext.proxyRequest.content 之间的区别。

例如:

request.content.asXML

context.proxyRequest.content.asXML

*.content*.content.asXML 形式在字符串上下文中都可以使用,JavaScript 会将其强制转换为字符串。在前一种情况 (*.content) 中,字符串包括所有声明以及 XML 注释。在后一种情况 (*.content.asXML) 中,结果的字符串值会清除声明和注释。

示例

msg.content

<?xml version="1.0" encoding="UTF-8"?>
<yahoo:error xmlns:yahoo="http://yahooapis.com/v1/base.rng" xml:lang="en-US">
   <yahoo:description>Please provide valid credentials. OAuth oauth_problem="unable_to_determine_oauth_type", realm="yahooapis.com"
   </yahoo:description>
</yahoo:error>
<!-- mg023.mail.gq1.yahoo.com uncompressed/chunked Sat Dec 14 01:23:35 UTC 2013 -->

msg.content.asXML

<?xml version="1.0" encoding="UTF-8"?>
<yahoo:error xmlns:yahoo="http://yahooapis.com/v1/base.rng" xml:lang="en-US">
   <yahoo:description>Please provide valid credentials. OAuth oauth_problem="unable_to_determine_oauth_type", realm="yahooapis.com"
   </yahoo:description>
</yahoo:error>

此外,您还可以使用 .asXML 形式通过指定元素和属性的名称来遍历 XML 层次结构。您无法使用其他语法遍历层次结构。

使用 JavaScript print() 语句进行调试

如果您使用 JavaScript 政策来执行自定义 JavaScript 代码,则可以使用 print() 函数将调试信息输出到 Trace 工具。此函数可直接通过 JavaScript 对象模型提供。例如:

if (context.flow=="PROXY_REQ_FLOW") {
     print("In proxy request flow");
     var username = context.getVariable("request.queryparam.user");
     print("Got query param: " + username);
     context.setVariable("USER.name", username);
     print("Set query param: " + context.getVariable("USER.name"));
}

if (context.flow=="TARGET_REQ_FLOW") {
     print("In target request flow");
     var username = context.getVariable("USER.name");
     var url = "http://mocktarget.apigee.net/user?"
     context.setVariable("target.url", url + "user=" + username);
     print("callout to URL: ", context.getVariable("target.url"));
}

要查看输出,请选择 Trace 窗口底部的从所有事务输出 (Output from all transactions)。您还可以在名为 stepExecution-stdout 的 Trace 属性中找到输出。

使用 httpClient 进行 JavaScript 调出

使用 httpClient 从在 API 代理流中执行的自定义 JavaScript 代码内向任意网址发送多个并行的异步 HTTP 请求。httpClient 对象由 Apigee JavaScript 对象模型公开。

httpClient 简介

httpClient 对象通过 JavaScript 对象模型,对在 Apigee 上运行的自定义 JavaScript 代码公开。要将自定义 JavaScript 附加到 API 代理,请使用 JavaScript 政策。当该政策运行时,自定义 JavaScript 代码会执行。

httpClient 对象可用于开发复合服务或混搭。例如,您可以将多个后端调用合并为一个 API 方法。此对象通常用作 ServiceCallout 政策的替代方法。

以下是基本用法。实例化一个请求对象,为其分配一个网址(例如,您要调用的后端服务),并使用该请求对象调用 httpClient.send

var myRequest = new Request();
myRequest.url = "http://www.example.com";
var exchangeObj = httpClient.send(myRequest);

httpClient 参考

HTTP 客户端提供两种方法:get()send()

httpClient.get()

用于简单的 HTTP GET 请求的便捷方法,不支持 HTTP 标头。

用量

var exchangeObj = httpClient.get(url);

返回结果

该方法会返回一个 exchange 对象。此对象没有属性,并提供以下方法:

  • isError():(布尔值)如果 httpClient 无法连接到服务器,则返回 true。当连接完成并且返回有效的响应代码,HTTP 状态代码 4xx5xx 会导致 isError() false。如果 isError() 返回 true,则对 getResponse() 的调用会返回 JavaScript undefined
  • isSuccess():(布尔值)如果发送完成且成功,则返回 true
  • isComplete():(布尔值)如果请求完成,则返回 true
  • waitForComplete():暂停线程,直到请求完成(成功或错误)。
  • getResponse():(对象)如果 httpClient.send() 完成并成功,则返回响应对象。返回的对象具有与 context.proxyResponse 对象相同的方法和属性。请参阅上下文对象摘要
  • getError():(字符串)如果对 httpClient.send() 的调用导致错误,则以字符串形式返回错误消息。

您可以在稍后使用 exchange 对象获取实际的 HTTP 响应,或查看响应是否超时。例如:

var ex1 = httpClient.get("http://www.example.com?api1");
context.session["ex1"] = ex1;  // Put the object into the session

var ex2 = httpClient.get("http://www.example.com?api2");
context.session["ex2"] = ex2;  // Put the object into the session

您可以稍后在流处理过程中访问返回的对象。在此例中,我们先从会话中获取交换对象,以便在另一个 JavaScript 政策中访问交换对象。

var ex1 = context.session["ex1"]; // Get exchange objs from the session.
var ex2 = context.session["ex2"];

ex1.waitForComplete();      // pause until the response is returned,
                            // or error occurs, or JS step time
                            // limit has been reached.
ex2.waitForComplete(100);   // pause for a maximum of 100 ms.

if (ex1.isSuccess() && ex2.isSuccess()) {
   response.content = ex1.getResponse().content + ex2.getResponse().content;
}

示例 1

var exchangeObj = httpClient.get("http://www.example.com");

httpClient.send()

允许您发送包含 HTTP 请求属性的完全配置的请求对象。

用量

var request = new Request(url, operation, headers);
var exchangeObj = httpClient.send(request);

返回结果

调用 httpClient.send() 会返回 exchange 对象。如需详细了解此对象,请参阅 httpclient.get() 方法说明。

示例 2

var headers = {'X-SOME-HEADER' : 'some value' };
var myRequest = new Request("http://www.example.com","GET",headers);
var exchange = httpClient.send(myRequest);

示例 3

var headers = {'Content-Type' : 'application/xml' };
var myRequest = new Request("http://www.example.com","POST",headers,""<foo>);
var exchange = httpClient.send(myRequest);

示例 4

调用后端服务,检索响应载荷,并从中提取变量。

/**
 * Retrieve an access token for the Microsoft Translator API
 * http://msdn.microsoft.com/en-us/library/hh454950.aspx
 */
function getAccessToken() {
  var bodyObj = {
    'grant_type': translatorApi.grantType,
    'scope': translatorApi.scopeUrl,
    'client_id': translatorApi.clientId,
    'client_secret': translatorApi.clientSecret
  };

  var req = new Request(translatorApi.authUrl, 'POST', {'Content-Type':'application/json'}, JSON.stringify(bodyObj));
  var exchange = httpClient.send(req);

  // Wait for the asynchronous POST request to finish
  exchange.waitForComplete();

  if (exchange.isSuccess()) {
    var responseObj = exchange.getResponse().content.asJSON;

    if (responseObj.error) {
      throw new Error(responseObj.error_description);
    }

    return responseObj.access_token;
  } else if (exchange.isError()) {
    throw new Error(exchange.getError());
  }
}

context.setVariable('twitter-translate.apiAccessToken', getAccessToken());

示例 5

跟踪 HTTP 请求的状态。

function userCheck() {
  var url = getAppServicesUrl() + '/users/' + username,
      headers = {
        Authorization : 'Bearer ' + appServicesAccessToken
      },
      req = new Request(url, 'GET', headers),
      exchange = httpClient.send(req),
      response, status;

  // Wait for the asynchronous GET request to finish
  exchange.waitForComplete();

  // get the response object from the exchange
  response = exchange.getResponse();

  // get the HTTP status code from the response
  status = response.status;

  if (status == 200) {
    context.setVariable('userCheck.trace', 'user exists');
  }
  else if (status == 404) {
    context.setVariable('userCheck.trace', 'user does not exist');
  }
  else {
    context.setVariable('userCheck.trace', 'user-inquiry-status:' + status);
  }
  return true;
}

示例 6:遍历响应标头

此有效示例展示了解如何遍历 httpClient.get() 响应中返回的响应标头。

以下是示例中使用的相关 JavaScript 代码:

var response = httpClient.get("http://mocktarget.apigee.net/json");
// set the pending request into a context variable
context.setVariable('pendingResponse', response);

var headers = "";

var response = context.getVariable('pendingResponse');
if (response) { // retrieve the pending request from the context variable
    response.waitForComplete();
    if (response.isSuccess()) {
        for (var n in response.getResponse().headers) {
            headers = headers + n + " --> " + response.getResponse().headers[n] + "\n";
        }
    }
    context.setVariable("response_headers", headers);
}

使用 JavaScript 政策

使用 JavaScript 政策将自定义 JavaScript 代码附加到代理流。另请参阅 GitHub 上的 Apigee 示例