处理错误

本页面适用于 ApigeeApigee Hybrid

查看 Apigee Edge 文档。

当 API 代理处理来自应用的请求时,可能会发生很多错误情况。例如,与后端服务通信时,API 代理可能会遇到网络问题,应用可能会呈现过期的凭据,请求消息的格式可能有误,等等。

客户端应用调用 API 代理后发生错误时,将向客户端返回错误消息。默认情况下,客户端收到经常加密但没有详细信息或指导的错误消息。但是,如果您想用更有用的自定义消息替换默认错误消息,甚至通过添加其他 HTTP 标头丰富信息,则需要在 Apigee 中设置自定义故障处理。

自定义故障处理还可让您在发生错误时添加消息日志记录等功能。

在我们介绍如何在 API 代理中实现自定义错误处理之前,了解错误是如何发生的,以及 API 代理如何对错误作出响应将很有帮助。

视频

观看以下视频,详细了解错误处理。

视频 说明
故障处理和错误流简介 了解故障处理以及 API 代理中出现错误时会出现什么情况。
使用故障规则处理故障 了解如何使用故障规则处理故障。
使用 RaiseFault 政策引发自定义故障 使用 RaiseFault 政策在 API 运行时引发自定义故障。
在 API 代理和目标端点中定义故障规则 在 API 代理和目标端点中定义故障规则,并了解其中的差异。
了解故障规则的执行顺序 了解 API 代理和目标端点中的故障规则的执行顺序。
定义默认故障规则 定义默认故障规则以处理 API 中的一般性错误。

错误发生方式

首先,我们仅介绍错误发生方式。 了解错误发生方式有助于您针对要实现自定义错误处理的不同情况做好计划。

自动错误

在以下情况下,API 代理会自动抛出错误:

  • 政策抛出错误。例如,如果 API 调用发送的密钥已过期,VerifyAPIKey 政策会自动抛出错误;或者如果 API 调用数量超过特定限制,配额政策SpikeArrest 政策将抛出错误。(请参阅政策错误参考文档,了解政策会抛出的错误类型。)
  • API 代理消息流出现问题,例如路由错误。
  • 存在后端故障,例如 HTTP 错误(由于协议级别故障、TLS/SSL 错误)或不可用的目标服务。
  • 存在系统级别故障,例如内存不足异常。

如需详细了解这些错误,请参阅本主题中的故障分类

自定义错误

对于没有自动错误的情况,您可能希望抛出自定义错误;例如,响应包含 unavailable 一词,或者 HTTP 状态代码大于 201 的情况。为此,可将 RaiseFault 政策添加到 API 代理流中的相应位置。

您可以向 API 代理流添加 RaiseFault 政策,其方式与任何其他政策相同。在以下代理配置示例中,Raise-Fault-1 政策将附加到 TargetEndpoint 响应。如果目标服务的响应中存在 unavailable 一词,则系统会执行 RiseFault 政策并抛出错误。

<TargetEndpoint name="default">
...
  <Response>
    <Step>
      <Name>Raise-Fault-1</Name>
      <Condition>message.content Like "*unavailable*"</Condition>
    </Step>
  </Response>

这只是为了向您展示您可以抛出自定义错误。我们将在 FaultRules 与 RaiseFault 政策部分中详细介绍 RaiseFault 政策。

如需查看更多示例,请参阅以下 Apigee 社区帖子:

发生错误时 API 代理执行的操作

以下是代理抛出错误时将发生的情况。

退出代理流水线

当 API 代理遇到错误时,无论发生了何种错误,都会退出正常流流水线,进入错误状态,并向客户端应用返回错误消息。一旦 API 代理进入错误状态,它便无法再将处理返回到正常流流水线。

例如,假设 API 代理在 ProxyEndpoint 请求中按以下顺序显示政策:

  1. Verify API Key
  2. 配额
  3. JSON to XML

如果 API 密钥验证期间发生错误,API 代理将进入错误状态。系统不会执行 Quota 和 JSON to XML 政策,代理不会继续进入 TargetEndpoint,并向客户端应用返回错误消息。

检查是否有 FaultRule

在错误状态中,API 代理还会在 API 代理配置中(按顺序)检查是否存在以下项,然后再将默认错误消息返回到客户端应用:

  1. <FaultRules> 部分,该部分会根据您定义的特定条件触发自定义错误消息(以及其他政策)的逻辑。
  2. <DefaultFaultRule> 部分,可在以下情况下触发默认错误消息:
    • 未定义 <FaultRules>
    • 未执行任何现有 <FaultRules>
    • <AlwaysEnforce> 元素设置为 true。

实质上,API 代理让您有机会返回自定义错误消息并触发其他逻辑。如果代理找不到这两个部分中的任一个,或者这些部分不存在,但系统未触发自定义故障,则代理会发送自己的 Apigee 生成的默认消息。

简单故障处理示例

我们先从一个简单的示例开始,其中调用 API 代理不包含所需的 API 密钥。默认情况下,下面是返回客户端应用的响应:

HTTP/1.1 401 Unauthorized
Date: Wed, 20 Jul 2016 19:19:32 GMT
Content-Type: application/json
Content-Length: 150
Connection: keep-alive
Server: Apigee Router

* Connection #0 to host myorg-test.apigee.net left intact
{"fault":{"faultstring":"Failed to resolve API Key variable request.queryparam.apikey","detail":{"errorcode":"steps.oauth.v2.FailedToResolveAPIKey"}}}

您的 API 用户或许能够找出错误消息,但他们可能没意识到。许多默认故障错误都变得更细微,更难以理解。

作为 API 开发者,您需要自行更改此消息,以满足谁最终会收到错误消息的需求,无论是 iOS 应用开发者,还是拥有自己的错误消息格式要求的内部测试群组。

下面的示例展示了您可如何创建自定义错误消息来处理此错误。这需要 1) 定义自定义消息的政策,以及 2) 代理进入错误状态时将执行政策的 FaultRule。

1. 创建定义自定义消息的政策

首先,创建一个政策以定义自定义错误消息。您可以使用任何类型的政策,例如可设置载荷和可选 HTTP 标头(例如状态代码和原因短语)的 AssignMessage 政策AssignMessage 政策最适合执行此操作。它允许您控制消息载荷、设置不同的 HTTP 状态代码、设置不同的 HTTP 原因短语,以及添加 HTTP 标头。

您无需将政策附加到流;只需按照创建政策中的说明创建政策即可。

以下是 AssignMessage 政策的示例:

  • 返回 JSON 消息。
  • 设置 HTTP 状态代码(911,这是显然不存在的状态代码,仅用于说明您拥有的灵活性)。状态代码会显示在 HTTP 标头中。
  • 设置 HTTP 原因短语(用于替换此缺失的 API 密钥错误的默认 Unauthorized 原因短语)。原因短语会显示在 HTTP 标头中的状态代码旁边。
  • 创建并填充名为 invalidKey 的新 HTTP 标头。
<AssignMessage async="false" continueOnError="false" enabled="true" name="invalid-key-message">
    <DisplayName>Invalid key message</DisplayName>
    <Set>
        <Payload contentType="application/json">{"Citizen":"Where's your API key? I don't see it as a query parameter"}</Payload>
        <StatusCode>911</StatusCode>
    </Set>
    <Add>
        <Headers>
            <Header name="invalidKey">Invalid API key! Call the cops!</Header>
        </Headers>
    </Add>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

执行此政策时,客户端应用的响应如下所示。将其与之前显示的默认响应进行比较。

HTTP/1.1 911 Rejected by API Key Emergency Services
Date: Wed, 20 Jul 2016 18:42:36 GMT
Content-Type: application/json
Content-Length: 35
Connection: keep-alive
invalidKey: Invalid API key! Call the cops!
Server: Apigee Router

* Connection #0 to host myorg-test.apigee.net left intact
{"Citizen":"Where's your API key? I don't see it as a query parameter."}

是,虽然有点不明智,但会向您显示各种可能情况。至少收到消息的开发者现在知道他们忘记将 API 密钥添加为查询参数。

但是,如何执行此政策?下一部分将为您介绍。

2. 创建将触发政策的 <FaultRule>

在代理配置的 <ProxyEndpoint><TargetEndpoint> 部分中,您将添加一个 <FaultRules> XML 块,其中包含一个或多个单独的 <FaultRule> 部分。每个 FaultRule 都代表您要处理的不同错误。在这个简单示例中,我们仅使用一个 FaultRule 来向您显示其组成。

如果未执行任何 FaultRule,您还应添加一个 <DefaultFaultRule> 以提供自定义常规错误消息。

示例

<ProxyEndpoint name="default">
...
    <FaultRules>
       <FaultRule name="invalid_key_rule">
            <Step>
                <Name>invalid-key-message</Name>
            </Step>
            <Condition>(fault.name = "FailedToResolveAPIKey")</Condition>
        </FaultRule>
    </FaultRules>
    <DefaultFaultRule name="default-fault">
        <Step>
            <Name>Default-message</Name>
        </Step>
    </DefaultFaultRule>

要点:

  • FaultRule 在 ProxyEndpoint 中定义。这很重要。稍后将详细介绍如何将 FaultRule 放入 ProxyEndpoint 与 TargetEndpoint。
  • <Name>:要执行的政策的名称。该名称来自父元素上的政策 name 属性,如先前政策所示。
  • <Condition>:仅在条件为 true 时,Apigee 才会评估条件并执行政策。如果有多个 FaultRule 计算结果为 true,则 Apigee 会执行第一条为 true 的规则。(重要提示:FaultRule 的评估顺序(从上到下,或从下到上),与 TargetEndpoint 和 ProxyEndpoint 的评估顺序不同,如 多个 FaultRule 和执行逻辑部分中所述)。如果您未添加条件,FaultRule 会自动设置为 true。但这不是最佳做法。每个 FaultRule 都有自己的条件。

  • <DefaultFaultRule>:如果未执行自定义 FaultRule,则系统会执行 <DefaultFaultRule>,从而发送更通用的自定义消息,而不是 Apigee 生成的默认加密消息。<DefaultFaultRule> 还可以包含 <Condition>,但在大多数情况下,您不会包含一个,因为您希望不管怎样都将它作为最后的手段执行。

    DefaultFaultRule 通常用于针对任何意外错误返回一般性错误消息。例如,包含技术支持的联系信息的消息。此默认响应是双刃剑,即提供开发者友好型信息,同时也会模糊后端网址或其他可能会被用于危害系统的信息。

多个 FaultRule 和执行逻辑

简单故障处理示例部分中,我们使用单个 FaultRule 和条件的简单示例。在实际 API 项目中,可能会发生所有可能的错误,您的 <ProxyEndpoint><TargetEndpoint> 中可能会有多个 FaultRule 和 DefaultFaultRule。但是,当 API 代理进入错误状态时,只会执行一个 FaultRule。

本部分介绍 Apigee 在处理 FaultRule 时使用的逻辑,从执行单个 FaultRule 的方式到触发 FaultRule 时处理内部步骤条件的方式。本部分还介绍了何时在 <ProxyEndpoint><TargetEndpoint> 中定义 FaultRule,并说明了 FaultRule 和 RaiseFault 政策之间的关系。

FaultRule 执行

简单来说,下面显示了 Apigee 会在 API 代理进入错误状态时使用的逻辑。请注意,ProxyEndpoint 与 TargetEndpoint 中的 FaultRule 评估略有不同。

  1. Apigee 会根据 ProxyEndpoint 或 TargetEndpoint 评估 FaultRule,具体取决于出现错误的位置:
    • ProxyEndpoint - Apigee 从配置 XML 中的 底部 <FaultRule> 开始,一直向下,评估每个 <FaultRule><Condition>(外部条件,而非内部 <Step> 条件)。
    • TargetEndpoint - Apigee 从配置 XML 中的 顶部 <FaultRule> 开始,一直向下,评估每个 <FaultRule><Condition>(外部条件,而非内部 <Step> 条件)。
  2. 执行第一个 FaultRule,其条件为 true。如果 FaultRule 没有条件,则默认为 true。
    • 执行 FaultRule 时,FaultRule 中的所有步骤都将按顺序进行评估,即 XML 配置从上到下。系统会自动执行没有条件的步骤(执行政策),并且会执行评估为 <Condition> 的步骤(不执行评估为 code 的条件)。
    • 如果执行 FaultRule,但未执行 FaultRule 中的任何步骤(因为其条件评估为 code),则 Apigee 生成的默认错误消息会返回至客户端应用。系统不会执行 <DefaultFaultRule>,因为 Apigee 已执行其一个 FaultRule。

  3. 如果未执行 FaultRule,则 Apigee 会执行 <DefaultFaultRule>(如果存在)。

以下是包含内嵌注释的示例。

ProxyEndpoint 执行

ProxyEndpoint FaultRule 从下到上评估,因此开始读取下面的示例中的最后一个 FaultRule 并一路向上执行。查看最后一个 DefaultFaultRule。

<ProxyEndpoint name="default">
...
    <FaultRules>
<!-- 3. This FaultRule is automatically TRUE, because there's no outer
     condition. But because the FaultRule just below this got
     executed (bottom-to-top evaluation in a ProxyEndpoint), Apigee
     doesn't even evaluate this FaultRule.
     Note that it's not a best practice to have a FaultRule without 
     an outer condition, which automatically makes the FaultRule true. -->
        <FaultRule name="random-error-message">
            <Step>
                <Name>Random-fault</Name>
            </Step>
        </FaultRule>
<!-- 2. Let's say this fault is TRUE. The Quota policy threw a QuotaViolation
     error. This is the first FaultRule to be TRUE, so it's executed. 
     Now the Steps are evaluated, and for the ones whose conditions
     evaluate to TRUE, their policies are executed. Steps without
     conditions are automatically true. -->
<FaultRule name="over_quota">
            <Step>
                <Name>developer-over-quota-fault</Name>
                <Condition>(ratelimit.developer-quota-policy.exceed.count GreaterThan "0")</Condition>
            </Step>
            <Step>
                <Name>global-over-quota-fault</Name>
                <Condition>(ratelimit.global-quota-policy.exceed.count GreaterThan "0")</Condition>
            </Step>
            <Step>
                <Name>log-error-message</Name>
            </Step>
            <Condition>(fault.name = "QuotaViolation")</Condition>
        </FaultRule>
<!-- 1. Because this is the ProxyEndpoint, Apigee looks at this FaultRule
     first. But let's say this FaultRule is FALSE. A policy did not 
     throw a FailedToResolveAPIKey error. Apigee moves UP to check
     the next FaultRule. -->
        <FaultRule name="invalid_key_rule">
            <Step>
                <Name>invalid-key-message</Name>
            </Step>
            <Condition>(fault.name = "FailedToResolveAPIKey")</Condition>
        </FaultRule>
    </FaultRules>

<!-- If no <FaultRule> is executed, the <DefaultFaultRule> is executed. 
     If a FaultRule is executed, but none of its Steps are executed,
     The DefaultFaultRule is not executed (because Apigee has already
     executed its one FaultRule). -->
    <DefaultFaultRule name="default-fault">
        <Step>
            <Name>Default-message</Name>
        </Step>
    </DefaultFaultRule>

TargetEndpoint 执行

TargetEndpoint FaultRule 从上到下评估,因此从以下示例中第一个 FaultRule 开始读取,并一路向下执行。查看最后一个 DefaultFaultRule。

<TargetEndpoint name="default">
...
    <FaultRules>
<!-- 1. Because this is the TargetEndpoint, Apigee looks at this FaultRule
     first. Let's say this FaultRule is FALSE. 
     A policy did not throw a FailedToResolveAPIKey error. 
     Apigee moves down to the next FaultRule. -->
        <FaultRule name="invalid_key_rule">
            <Step>
                <Name>invalid-key-message</Name>
            </Step>
            <Condition>(fault.name = "FailedToResolveAPIKey")</Condition>
        </FaultRule>
<!-- 2. Let's say this fault is TRUE. The Quota policy threw a QuotaViolation
     error. This is the first FaultRule to be TRUE, so it's executed. 
     Now the Steps are evaluated, and for the ones whose conditions
     evaluate to TRUE, their policies are executed. Steps without
     conditions are automatically true. -->
        <FaultRule name="over_quota">
            <Step>
                <Name>developer-over-quota-fault</Name>
                <Condition>(ratelimit.developer-quota-policy.exceed.count GreaterThan "0")</Condition>
            </Step>
            <Step>
                <Name>global-over-quota-fault</Name>
                <Condition>(ratelimit.global-quota-policy.exceed.count GreaterThan "0")</Condition>
            </Step>
            <Step>
                <Name>log-error-message</Name>
            </Step>
            <Condition>(fault.name = "QuotaViolation")</Condition>
        </FaultRule>
<!-- 3. This FaultRule is automatically TRUE, because there's no outer
     condition. But because the FaultRule just above this got
     executed (top-to-bottom evaluation in a TargetEndpoint), Apigee
     doesn't even evaluate this FaultRule.
     Note that it's not a best practice to have a FaultRule without 
     an outer condition, which automatically makes the FaultRule true. -->
        <FaultRule name="random-error-message">
            <Step>
                <Name>Random-fault</Name>
            </Step>
        </FaultRule>
    </FaultRules>

<!-- If no <FaultRule> is executed, the <DefaultFaultRule> is executed. 
     If a FaultRule is executed, but none of its Steps are executed,
     The DefaultFaultRule is not executed (because Apigee has already
     executed its one FaultRule). -->
    <DefaultFaultRule name="default-fault">
        <Step>
            <Name>Default-message</Name>
        </Step>
    </DefaultFaultRule>

故障规则顺序

如上例所示,放置 FaultRule 的顺序很重要,具体取决于错误是出现在 ProxyEndpoint 中还是 TargetEndpoint 中。

例如:

ProxyEndpoint 顺序 TargetEndpoint 顺序

在以下示例中,由于从下到上评估,因此执行 FaultRule 3,这意味着不评估 FaultRule 2 和 1。

5. FaultRule 1:FALSE

4. FaultRule 2:TRUE

3. FaultRule 3:TRUE

2. FaultRule 4:FALSE

1. FaultRule 5:FALSE

在以下示例中,由于从上到下评估,因此执行 FaultRule 2,这意味着不评估 FaultRule 3、4 和 5。

1. FaultRule 1:FALSE

2. FaultRule 2:TRUE

3. FaultRule 3:TRUE

4. FaultRule 4:FALSE

5. FaultRule 5:FALSE

要包含的政策

您可以通过将政策放入“步骤”中,执行 FaultRule 中的任意政策。例如,您可以执行 AssignMessage 政策来设置对客户端应用的响应的格式,然后使用 MessageLogging 政策记录消息。政策会按照您添加它们的顺序(即在 XML 中从上到下)执行。

故障规则仅在错误状态下触发(关于 continueOnError)

标题可能貌似我们自己反复重复,但对于导致 API 代理进入错误状态或未进入错误状态的代理错误,系统会特别注意特定错误:政策中的 continueOnError 属性。

总而言之,仅当代理进入错误状态时,API 代理才会评估 <FaultRules><DefaultFaultRule>这意味着,即使 FaultRule 条件的评估结果为 true,也不会触发(如果代理未处于错误状态)。

但是,下面的示例显示了出现错误但代理未进入错误状态的示例。在任何政策上,您都可以对名为 continueOnError 的父元素设置属性。该属性对于故障处理非常重要,因为它决定着代理是否会在政策失败时进入错误状态。在大多数情况下,您需要保留默认的 continueOnError="false",如果代理发生故障,将使代理进入错误状态,并且触发自定义错误处理。但是,如果 continueOnError="true"(例如,如果您不希望 Service Callout 无法停止代理执行),则在该政策失败时代理不会进入错误状态,并且代理不会查看您的 FaultRule。

如需了解如何在 continueOnError="true" 时记录错误,请参阅处理当前流中的政策故障

FaultRule 定义位置:ProxyEndpoint 或 TargetEndpoint

如果 API 代理遇到错误,则错误会在 <ProxyEndpoint>(客户端应用的请求或响应)中或 <TargetEndpoint> 中(目标服务的请求或响应)发生。Apigee 将在错误发生位置查找 FaultRule。

例如,如果目标服务器不可用(HTTP 状态代码 503),则 API 代理将在 <TargetEndpoint> 响应中进入错误状态,并且正常的 API 代理流程不会转到 <ProxyEndpoint>。如果您仅在 <ProxyEndpoint> 中定义 FaultRule,则它们不会处理该错误。

再看下面一个示例。如果 <ProxyEndpoint> 响应上的 RaiseFault 政策触发错误,系统将不会执行 <TargetEndpoint> 中的 FaultRule。

FaultRule 与 RaiseFault 政策

故障规则和 RaiseFault 政策可能在表面上听起来像完成故障处理的替代方式。某些情况下的确如此。但它们可以协同工作。本部分介绍两者之间的关系。了解此关系应该有助于您设计故障处理,尤其是当您想要使用这两种方案时。

简而言之:

  • 当 API 代理进入错误状态时,系统会始终评估故障规则
  • RaiseFault 政策是一种在本不会发生错误的情况下将 API 代理置于错误状态的方式。

    例如,如果要在目标服务的响应中的 HTTP 状态代码大于 200 时抛出错误,请在响应流中添加 RaiseFault 政策。这如下所示:

    <TargetEndpoint name="default">
        <PreFlow name="PreFlow">
    ...
            <Response>
                <Step>
                    <Name>Raise-Fault-1</Name>
    <!-- If the condition is true, the Raise-Fault-1 policy gets executed -->
                    <Condition>(response.status.code GreaterThan "200")</Condition>
                </Step>
            </Response>

    RaiseFault 政策还会向客户端应用发送错误消息。

当 RaiseFault 政策触发错误时,错误会将代理置于错误状态,这可能会执行 FaultRule,那将会怎样?这种情况可能稍微有点复杂。如果 RaiseFault 政策返回错误消息并且触发 FaultRule 并返回错误消息,则系统会向客户端应用返回哪些内容?

  • 由于在 RaiseFault 政策后执行 FaultRule 或 DefaultFaultRule,因此 FaultRule 响应数据会胜出。
  • 如果 FaultRule 或 DefaultFaultRule 未设置该等数据,系统将使用 RaiseFault 政策响应数据(状态代码、原因短语或消息载荷)。
  • 如果 RaiseFault 政策和 FaultRule 都添加自定义 HTTP 标头,则响应中会包含这两者。重复的标头名称会创建一个包含多个值的标头。

以下示例展示了 RaiseFault 政策和 FaultRule 设置的内容,以及返回给客户端应用的内容。这些示例专为简洁起见而设计,并非最佳做法。

由 RaiseFault 政策和 FaultRule 设置的内容。

客户端应用接收

Status Code: 468
Reason Phrase: Something happened
Payload: {"Whoa":"Sorry."}
Header:
  errorNote: woops,gremlins

<- 故障规则政策会设置以下内容

Status Code: [none] 
Reason Phrase: Something happened
Payload: {"Whoa":"Sorry."}
Header:
  errorNote: gremlins

<- RaiseFault 政策会设置以下内容

Status Code: 468
Reason Phrase: Can't do that
Payload: {"DOH!":"Try again."}
Header: 
  errorNote: woops

构建条件

条件是 FaultRule 执行的关键。您可以像在 Apigee 中的其他条件那样创建 FaultRule 条件,例如条件流或 RaiseFault 条件。

若需将此部分的其余内容置于上下文,可以参考下面具有外部 FaultRule 条件和内部步骤条件的故障规则示例。

<FaultRule name="invalid_key_rule">
    <Step>
        <Name>invalid-key-message</Name>
        <Condition>oauthV2.Verify-API-Key-1.failed = true</Condition>
    </Step>
    <Condition>fault.name = "FailedToResolveAPIKey"</Condition>
</FaultRule>

政策错误特有的变量

当政策抛出错误时,fault.name{policy_namespace}.{policy_name}.failed 变量将可用。

fault.name

当政策失败时,使用 fault.name 变量捕获条件中的错误。例如:

<Condition>fault.name = "policy_error_name"</Condition>

错误名称显示在默认错误消息中。例如,在以下示例中,故障名称为 FailedToResolveAPIKey。在本例中,名为 fault.name 的流变量设置为值 FailedToResolveAPIKey

{"fault":{"faultstring":"Failed to resolve API Key variable request.queryparam.apikey","detail":{"errorcode":"steps.oauth.v2.FailedToResolveAPIKey"}}}

因此,条件如下所示:

<Condition>fault.name = "FailedToResolveAPIKey"</Condition>

请参阅政策错误参考文档,查看政策错误列表。

{policy_namespace}.{policy_name}.failed

*.failed 变量将在政策失败时可用。下面是适用于不同政策的 *.failed 变量示例。对于政策命名空间,请参阅每个政策参考主题中的流变量。

其他可用变量

当 API 代理进入错误状态时,可用于条件的变量仅有:

  • 失败的政策的变量。
  • 故障点存在的 HTTP 消息变量。例如,如果响应中抛出错误,<TargetEndpoint> 中的 FaultRule 可以使用 HTTP 数据 response.status.codemessage.contenterror.content 等等。或者,如果配额政策失败,您可以使用变量 ratelimit.{quota_policy_name}.exceed.count。使用调试工具政策参考文档有助于确定哪些变量和 HTTP 数据可用。

更多信息

故障处理最佳做法

故障处理是 API 代理开发的主要架构设计任务。请务必花些时间了解错误处理方式和时间、确定会显示哪些错误消息,以及设计错误消息格式。您理顺这些状况之后(或期间),可利用这些最佳做法帮助实现故障处理。

下面是设计和构建故障处理方面的一些最佳做法:

  • 在 FaultRule 中,您可以指定任何类型的政策。最常见的模式是使用 AssignMessage 政策设置待处理错误响应中的特定项目。您还可以使用 AssignMessage 设置用于其他用途的变量,例如,用于在 PostClientFlow 或 FlowHooks 中执行的日志记录政策引用的变量。 例如,如果您要记录特定故障条件中的特定错误,您还可以考虑使用 MessageLogging 政策ServiceCallout 政策来记录消息。
  • 请勿将 RaiseFault 政策指定为 FaultRule 中的步骤。最好使用 AssignMessage 政策来设置或更改消息元素,包括载荷、标头或状态代码。
  • 对于每个 FaultRule 或除了最后评估的 FaultRule 之外,再提供一个外部 <Condition> 作为 <FaultRule> 元素的子元素。未指定显式 Condition 的 FaultRule 执行条件将隐式评估为 true。附加为 <Step> 元素的子元素的 <Condition> 元素用于确定 FaultRule 的执行条件是评估为 true 还是 false。仅当在 Apigee 执行包含步骤条件的 FaultRule 之后,系统才会评估步骤条件。在 FaultRule 中,通常有多个步骤包含 AssignMessage(或其他)政策,每个步骤都有一个步骤条件。
  • 如需处理同一类型的多个政策(例如多个配额政策)中的错误,请针对您可能收到的每个政策错误创建一个 FaultRule,然后使用附加到步骤的条件来区分单独的错误。例如,创建一个 FaultRule 来处理配额政策中的一个错误(例如 QuotaViolation),并创建一个单独的 FaultRule 来处理 InvalidApiKey。 (请参阅政策错误参考文档,了解政策错误。发现需要处理的其他错误时,您可以稍后返回并将其添加至 FaultRule。可以迭代,但需要代理重新部署。)这种方法允许您捕获同一类型的错误,而无论哪个政策抛出错误,这将使得 FaultRule XML 效率更高。

    内部步骤条件可为您提供更精细的控制。例如,如果您在请求流中使用两个政策强制执行单个开发者配额和全局配额,则设置外部 FaultRule 条件,以在出现 QuotaViolation 错误时触发(无论哪一种情况,超过配额都会抛出该错误)。然后,设置步骤条件以评估两个配额政策中的特定 exceed.count 变量。只会将相关错误发送到客户端(开发者配额超额或全局配额超额)。下面是此配置的一个示例:

    <FaultRule name="over_quota">
      <!-- This condition catches a QuotaViolation in *any* Quota policy -->
      <Condition>fault.name = "QuotaViolation"</Condition>
      <Step>
        <Name>AM-developer-over-quota-fault</Name>
        <Condition>ratelimit.developer-quota-policy.exceed.count GreaterThan 0</Condition>
      </Step>
      <Step>
        <Name>AM-global-over-quota-fault</Name>
        <Condition>ratelimit.global-quota-policy.exceed.count GreaterThan 0</Condition>
      </Step>
    </FaultRule>

    如需查看其他示例,请参阅有关政策故障处理的讨论

  • 如需在使用一个类型的单个政策时处理错误,请考虑将在一个政策失败时执行的单个故障规则,并且包含映射到各个可能错误的多个步骤。这将使用单个 FaultRule 而不是多个 FaultRule(每个错误类型一个),从而让 XML 保持简单。例如,您可以指定不同的 AssignMessage 政策步骤在不同条件下执行,如下所示:

    <FaultRule name="raise-fault-3">
      <!-- This condition catches *any* error in the Verify-API-Key-1 policy. -->
      <Condition>oauthV2.Verify-API-Key-1.failed = "true"</Condition>
      <!-- This first step always executes, which handles errors you haven't mapped with inner conditions. -->
      <Step>
        <Name>AM-Generic-Key-Fault</Name>
      </Step>
      <Step>
        <Name>AM-API-Key-NotFound</Name>
        <Condition>fault.name = "FailedToResolveAPIKey"</Condition>
      </Step>
      <Step>
        <Name>AM-API-Key-Invalid</Name>
        <Condition>fault.name = "InvalidApiKey"</Condition>
      </Step>
    </FaultRule>
  • 添加出现错误的 FaultRule(客户端 <ProxyEndpoint> 或目标端 <TargetEndpoint>)。针对出现在每个位置的每个政策添加 FaultRule。
  • 将 RaiseFault 政策与 FaultRule 搭配使用时,请协调在 RaiseFault 政策和 FaultRule 返回数据时发回的响应数据。例如,如果您具有用于设置 HTTP 状态代码的 RaiseFault 政策,请不要在用于重置状态代码的 FaultRule 中配置 AssignMessage 步骤。最糟糕的情况是,默认状态代码会返回到客户端应用。
  • <DefaultFaultRule> 元素是对 <FaultRules> 元素的补充,使您能够更好地控制代理在处理故障状态时执行的政策。如果您指定 <DefaultFaultRule>,则当同时满足以下两个条件或满足其中一个条件时,它将执行:

    • 没有其他 FaultRule 已执行。一种特殊情况是,根本没有配置 <FaultRules> 元素。
    • 如果 <DefaultFaultRule><AlwaysEnforce> 子元素为 true。

    您还可以在 <DefaultFaultRule> 上指定 <Condition> 元素。 您可能希望这么做以根据请求或待处理错误消息的某些状态(例如,如果存在或缺少特定标头)来排除元素的执行。

    使用 <AlwaysEnforce> 设置为 true<DefaultFaultRule>(如果您有一个或多个希望代理始终执行的政策,无论先前的 FaultRule 是否已执行)。一种可能的场景:假设您想要在所有情况下将标头注入响应中,无论代理请求是否导致故障,也无论之前是否处理了故障。于是,您将在 <PostFlow>/<Response> 部分中附加适当的 AssignMessage 政策,并且在 <AlwaysEnforce> 设置为 true<DefaultFaultRule> 中附加同一政策。

集中式、可重复使用的故障处理模式

Apigee 代理的错误处理模式介绍了一种无需进行代码复制即可集中处理故障的模式:

创建 FaultRule

如需添加 FaultRule,您需要修改 ProxyEndpoint 或 TargetEndpoint 的 XML 配置。您可以使用 Apigee 界面在 API 代理开发视图的代码窗格中进行此类修改,或者修改定义 ProxyEndpoint 或 TargetEndpoint 的 XML 文件。

如果您在 Apigee 界面中创建 FaultRule,请先创建要执行的政策,然后将其添加到 FaultRule 配置中。(如果您尝试保存引用尚未创建的政策的 FaultRule,您将在界面中收到错误消息。)

将政策添加到 FaultRule

虽然您可以将任何政策放入 FaultRule,但通常使用 AssignMessage 政策为错误条件生成自定义响应消息。借助 AssignMessage,您可以使用载荷、HTTP 状态代码、标头和原因短语元素配置 HTTP 响应。

以下示例展示了典型的 AssignMessage 政策配置:

<AssignMessage name="AM-Invalid-Key">
  <Set>
      <Payload contentType="text/plain">That is an error.</Payload>
      <StatusCode>401</StatusCode>
  </Set>
  <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</AssignMessage>

请注意,它未指定 <AssignTo> 元素。这意味着它将分配给环境消息,具体取决于政策附加位置。

您现在可以在 FaultRule 中使用此政策。注意如何在 FaultRule 中按名称引用 AssignMessage 政策:

<ProxyEndpoint name="default">
  ...
  <FaultRules>
    <FaultRule name="invalid_key_rule">
      <Step>
        <Name>AM-Invalid-Key</Name>
      </Step>
      <Condition>fault.name = "InvalidApiKey"</Condition>
    </FaultRule>
  </FaultRules>
</ProxyEndpoint>

部署上述配置时,每当应用提供无效的 API 密钥时,API 代理都会执行名为 AM-Invalid-Key 的 AssignMessage 政策。

您可以在 FaultRule 中执行多个政策,如以下示例所示:

<ProxyEndpoint name="default">
  ...
  <FaultRules>
    <FaultRule name="invalid_key_rule">
      <Step>
        <Name>AM-Invalid-Key</Name>
      </Step>
      <Step>
        <Name>policy2</Name>
      </Step>
      <Step>
        <Name>policy3</Name>
      </Step>
      <Condition>fault.name = "InvalidApiKey"</Condition>
    </FaultRule>
  </FaultRules>
</ProxyEndpoint>

政策会按定义的顺序执行。例如,您可以在 FaultRule 中使用 MessageLogging 政策ExtractVariables 政策AssignMessage 政策或任何其他政策。请注意,如果出现以下任一情况,对 FaultRule 的处理会立即停止:

  • FaultRule 中的任何政策都会导致错误
  • FaultRule 中的任何政策都是 RaiseFault 类型

定义从 FaultRule 返回的自定义错误消息

最佳做法是,应通过 API 定义明确的错误响应。这样,您就能为客户提供一致的实用信息。

以下 AssignMessage 政策示例使用 <Payload><StatusCode> 标记来定义在发生 InvalidApiKey 错误时将发回到客户端的自定义错误响应(请参阅上一个 FaultRule 示例)。

<AssignMessage name="AM-Invalid-Key">
  <Set>
    <Payload contentType="text/plain">You have attempted to access a resource without the correct authorization.
       Contact support at support@mycompany.com.</Payload>
    <StatusCode>401</StatusCode>
  </Set>
  <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</AssignMessage>

此响应包括:

  • 包含错误消息和用于联系支持团队的电子邮件地址的载荷。
  • 响应中返回的 HTTP 状态代码。
  • 原因短语,是对错误的简短说明。

创建 DefaultFaultRule

DefaultFaultRule 可充当其他 FaultRule 未显式处理的任何错误的异常处理程序。如果所有 FaultRule 的条件都与错误不匹配,则 DefaultFaultRule 会处理错误。通过将 <DefaultFaultRule> 标记添加为 ProxyEndpoint 或 TargetEndpoint 的子元素,启用默认故障处理。

例如,下面的 TargetEndpoint 配置定义了一个将调用名为 AM-Return-Generic-Error 的政策的 DefaultFaultRule:

<TargetEndpoint name="default">
  ...
  <FaultRules>
    ...
  </FaultRules>

  <DefaultFaultRule name="fault-rule">
    <Step>
      <Name>AM-Return-Generic-Error</Name>
    </Step>
  </DefaultFaultRule>

  <HTTPTargetConnection>
    <URL>https://mytarget.example.net</URL>
  </HTTPTargetConnection>
</TargetEndpoint>

DefaultFaultRule 通常用于针对任何意外错误返回一般性错误消息,例如包含技术支持的联系信息的消息。此默认响应是双刃剑,即提供开发者友好型信息,同时也会模糊后端网址或其他可能会被用于危害系统的信息。

例如,您定义了以下 AssignMessage 政策来返回一般性错误:

<AssignMessage name="AM-Return-Generic-Error">
  <Set>
    <Payload type="text/plain">SERVICE UNAVAILABLE. PLEASE CONTACT SUPPORT: support@company.com.</Payload>
  </Set>
</AssignMessage>

<DefaultFaultRule> 标记中包含 <AlwaysEnforce> 元素即可针对每个错误执行 DefaultFaultRule,即使已执行其他 FaultRule 也是如此。DefaultFaultRule 始终是执行的最后一个 FaultRule:

  <DefaultFaultRule name="fault-rule">
    <Step>
      <Name>AM-Return-Generic-Error</Name>
    </Step>
    <AlwaysEnforce>true</AlwaysEnforce>
  </DefaultFaultRule>

DefaultFaultRule 的一个用途是确定您无法确定的错误类型。例如,如果您的 API 代理因无法确定的错误而失败,您可以使用 DefaultFaultRule 来调用以下 AssignMessage 政策。此政策会将 fault.name 值写入响应中名为 Unhandled-Fault 的标头:

<AssignMessage name="AM-Set-Fault-Header">
  <Set>
    <Headers>
      <Header name="Unhandled-Fault">{fault.name}</Header>
    </Headers>
  </Set>
  <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</AssignMessage>

然后您可以在调试工具或响应中查看标头,了解导致错误的原因。

将消息日志记录添加到 PostClientFlow

PostClientFlow 是代理进入错误状态后执行的唯一流。只有 MessageLogging 政策可以附加到此流中,在将响应发送回客户端后会执行该流。虽然将 MessageLogging 政策附加到此流在技术上并不是错误处理,但您可以用它来记录发生错误时的相关信息。由于无论代理是成功还是失败,系统都会执行它,因此您可以将 Message Logging 政策放在 PostClientFlow 中,并保证它们始终执行。

处理当前流中的政策故障

到目前为止显示的示例均使用 ProxyEndpoint 或 TargetEndpoint 上的 FaultRule 处理错误状态中的任何政策错误。这是因为政策的 continueOnError 元素的默认值是 false,这意味着政策中出现错误时,控制权会被定向到错误状态。一旦进入错误状态,便无法将控制权返回至正常流水线,您通常会向调用应用返回某种形式的错误消息。

但是,如果您为政策将 continueOnError 元素设置为 true,则控制权会留在当前流中,并且流水线中的下一个政策会在导致错误的政策后执行。处理当前流中的错误的优势在于,您可以从错误中恢复,以完成请求处理。

下面显示了一个名为 verify-api-keyVerifyAPIKey 政策,其中 continueOnError 元素设置为 true:

<VerifyAPIKey continueOnError="true" name="verify-api-key">
  <DisplayName>Verify API Key</DisplayName>
  <APIKey ref="request.queryparam.apikey"/>
</VerifyAPIKey>

如果 API 密钥缺失或无效,则 VerifyAPIKey 政策会将 oauthV2.verify-api-key.failed 变量设置为 true,但会在当前流中继续处理。

然后,您将 VerifyAPIKey 政策添加为 ProxyEndpoint 的 PreFlow 中的一个步骤:

<ProxyEndpoint name="default">
  ...
  <PreFlow name="PreFlow">
    <Request>
      <Step>
        <Name>verify-api-key</Name>
      </Step>
      <Step>
        <Name>FaultInFlow</Name>
        <Condition>oauthV2.verify-api-key.failed = "true"</Condition>
      </Step>
    </Request>
    <Response/>
  </PreFlow>      
</ProxyEndpoint>  

请注意 PreFlow 中的下一步如何使用条件来测试是否存在错误。如果 VerifyAPIKey 政策发生错误,则执行名为 FaultInFlow 政策的政策。否则,将跳过 FaultInFlow 政策。FaultInFlow 政策可执行多种操作,例如记录错误、尝试修复错误或执行其他一些操作。

使用 RaiseFault 政策触发错误

您可以随时在流中使用 RaiseFault 政策来触发错误。当 RaiseFault 政策执行时,它会终止当前流并将控制权转移到错误状态。

RaiseFault 政策的一个用途是测试其他政策可能无法检测的特定条件。在上面的示例中,您在 PreFlow <Step> 标记中添加了 <Condition> 标记,导致会在满足条件时执行 FaultInFlow 政策。如果 FaultInFlow 是 RaiseFault 政策,则控制权将转移到错误状态。或者,您可以在流中插入 RaiseFault 政策,以调试和测试 FaultRule。

当 RaiseFault 政策触发错误时,您可以使用以下 FaultRule 和条件来处理错误:

<FaultRule name="raisefault_rule">
  <Step>
    <Name>POLICY-NAME-HERE</Name>
  </Step>
  <Condition>fault.name = "RaiseFault"</Condition>
</FaultRule>

请注意,条件会测试是否存在名为 RaiseFault 的故障。RaiseFault 政策始终会将 fault.name 的值设置为 RaiseFault。您还可以在 RaiseFault 政策中设置自定义变量。如果这样做,您可以在 Condition 元素中测试这些变量。

来自目标服务器的 HTTP 错误代码自定义处理

前面部分中显示的示例也适用于政策造成的错误。但是,您也可以为传输级别错误创建自定义响应,这意味着从目标服务器返回的 HTTP 错误。如需控制来自 HTTP 错误的响应,请配置 TargetEndpoint 以处理 HTTP 响应代码。

默认情况下,Apigee 会将 1xx-3xx 范围内的 HTTP 响应代码视为成功,将 4xx-5xx 范围内的 HTTP 响应代码视为失败。这意味着来自使用 HTTP 响应代码 4xx-5xx 的后端服务的任何响应都会自动调用错误状态,然后会直接向请求客户端返回错误消息。

您可以为任何 HTTP 响应代码创建自定义处理程序。例如,您可能不想将 4xx-5xx 范围内的所有 HTTP 响应代码视为“失败”,而仅将 5xx 视为失败,也可能想为 HTTP 响应代码 400500 返回自定义错误消息。

在下一个示例中,您使用 success.codes 属性将 TargetEndpoint 配置为将 HTTP 响应代码 400500 与默认 HTTP 代码一起视为成功。通过将这些代码视为成功,TargetEndpoint 会接管响应消息的处理,而不是调用错误状态:

<TargetEndpoint name="default">
  ...
  <HTTPTargetConnection>
    <Properties>
          <Property name="success.codes">1xx,2xx,3xx,400,500</Property>
    </Properties>
    <URL>http://weather.yahooapis.com</URL>
  </HTTPTargetConnection>
</TargetEndpoint>

如本例所示,您可以使用通配符将 success.codes 属性设置为一个值范围。

设置 success.codes 属性会覆盖默认值。因此,如果您想将 HTTP 代码 400 添加到默认成功代码列表中,请将此属性设置为:

<Property name="success.codes">1xx,2xx,3xx,400</Property>

但是,如果您只想将 HTTP 代码 400 视为成功代码,请将此属性设置为:

<Property name="success.codes">400</Property>

现在,您可以为 HTTP 响应代码 400500 定义自定义处理程序,以便向请求应用返回自定义的响应消息。以下 TargetEndpoint 使用名为 ReturnError 的政策来处理 HTTP 400500 响应代码:

<TargetEndpoint name="default">
  <PreFlow name="PreFlow">
    <Request/>
    <Response>
      <Step>
        <Name>ReturnError</Name>
        <Condition>(response.status.code = 400) or (response.status.code = 500)</Condition>
      </Step>
    </Response>
  </PreFlow>

  <HTTPTargetConnection>
    <Properties>
      <Property name="success.codes">1xx,2xx,3xx,400,500</Property>
    </Properties>
    <URL>http://weather.yahooapis.com</URL>
  </HTTPTargetConnection>
</TargetEndpoint>

每当 TargetEndpoint 遇到 HTTP 响应代码 400500 时,此 TargetEndpoint 配置都会使名为 ReturnError 的政策处理响应。

故障分类

API 服务将错误分为以下类别和子类别。

类别 子类别 故障名称 说明
消息传送 消息流期间发生的故障(不包括政策故障)
自定义故障 {fault_name} API 代理使用 RaiseFault 政策显式处理的任何错误
响应代码 InternalServerError、NotFound HTTP 错误代码 5xx4xx
路由失败 NoRoutesMatched 为请求选择指定的 TargetEndpoint 时出现故障
分类故障 NotFound 与任何 ProxyEndpoint 配置的任何 BasePath 不匹配的请求 URI(即没有 API 代理与客户端应用的请求中的网址匹配)导致的故障
传输 HTTP 传输级别错误
连接 ConnectionRefused、ConnectionReset、ConnectionTimeout 建立网络或传输级别连接时出现故障
请求验证 ContentLengthMissing、HostHeaderMissing 每次请求进行语义检查时都出现故障
响应验证 每次响应进行语义检查时都出现故障
IO 错误 SSLHandshakeError、ReadTimeout、ReadError、WriteTimeout、WriteError、ChunkError 客户端或目标端点的读写错误、超时、TLS/SSL 错误和分块错误
系统 未定义的运行时错误
内存 OutOfMemory、GCOverLimit 内存相关故障
会话 RogueTaskTerminated 正在运行的任务终止等故障
政策 政策参考文档中定义了每种政策类型的故障。

错误始终附有故障原因的文本说明。如果系统发生故障,系统会填充一组属性来帮助进行问题排查。故障包括以下信息:

  • 原因
  • 用户定义的自定义属性