500 内部服务器错误 - BadFormData

您正在查看 Apigee Edge 文档。
前往 Apigee X 文档。
info

问题

客户端应用收到 HTTP 状态代码 500 Internal Server Error,并收到错误代码 protocol.http.BadFormData,以此响应 API 调用。

出错提示

客户端应用获取以下响应代码:

HTTP/1.1 500 Internal Server Error

此外,您可能会看到以下错误消息:

{
   "fault":{
      "faultstring":"Bad Form Data",
      "detail":{
         "errorcode":"protocol.http.BadFormData"
      }
   }
}

表单数据

在详细介绍如何排查此问题之前,我们先来了解一下什么是表单数据。

表单数据是用户通常通过包含文本输入框、按钮或复选框等元素的 HTML 表单提供的信息 。表单数据通常作为一系列 键值对发送,作为 HTTP 请求或响应的一部分。

表单数据传输

  1. Content-Type:application/x-www-form-urlencoded
    • 如果表单数据的大小较小,则数据将作为键值对发送,其中:

      包含表单数据的示例请求:

      curl https://HOSTALIAS/somepath -H "Content-Type: application/x-www-form-urlencoded" -d "username=abc@google.com&pasword=secret123"
      
    • 两个键和值中的任何非字母数字字符都经过 百分号编码,也就是说,它们表示为字符三元组 %HH,由百分号后跟两个十六进制数字组成,这两个十六进制数字表示特定字符的 ASCII 代码。
    • 因此,即使表单数据中允许使用百分号 (%),它也会被解读为特殊转义序列的开头。因此,如果表单数据需要在 键或值中包含百分号 (%),则应将其作为 %25, 传输,这表示百分号 (%) 字符的 ASCII 代码。
  2. Content-Type:multipart/form-data

    如果您要传输大量二进制数据或包含非 ASCII 字符的文本,则可以使用 Content-Type: multipart/form-data 发送数据,如 表单 - 第 17.13.4.2 节中所述

可能的原因

当且仅当满足以下所有条件时,会出现此错误:

  1. 客户端向 Apigee Edge 发送的 HTTP 请求 包含:
    1. Content-Type: application/x-www-form-urlencoded,以及
    2. 带有百分号 (%) 或百分号 (%) 后跟无效的十六进制字符的表单数据,这些字符根据 表单 - 第 17.13.4.1 节是不允许的。
  2. Apigee Edge 中的 API 代理会读取特定表单参数,其中包含请求流中使用 ExtractVariables 或 AssignMessage 政策 不允许 使用的任何字符。

    例如,如果表单数据包含百分号 (%) 原样(不编码)或百分号 (%) 后跟键和/或值中的任何无效十六进制字符,则会收到此错误。

    以下是造成此错误的可能原因:

    原因 说明 适用的问题排查说明
    请求中的表单参数包含不允许使用的字符 客户端作为 HTTP 请求的一部分传递的表单参数包含任何 不允许使用的字符。 Edge Public 和 Private Cloud 用户

常见诊断步骤

请使用以下工具/方法之一来诊断此错误:

API 监控

如需使用 API 监控诊断错误,请执行以下操作:

  1. 以具有 适当角色的用户身份登录 Apigee Edge 界面。
  2. 切换到您要调查问题的组织。

  3. 依次前往 Analyze > API Monitoring > Investigate 页面。
  4. 选择您观察到错误的具体时间范围。
  5. 绘制 Fault Code 与 Time 的关系图。

  6. 选择包含故障代码 protocol.http.BadFormData 的单元格,如下所示:

    (查看放大图片)

  7. 系统会显示有关故障代码 protocol.http.BadFormData 的信息,如下所示:

    (查看放大图片)

  8. 点击 View logs ,然后展开失败请求的行。

  9. 在 Logs 窗口中,记下以下详细信息:
    • Status Code: 500
    • Fault Source: proxy
    • Fault Code: protocol.http.BadFormData
    • Fault Policy: extractvariables/EV-ExtractFormParams
  10. 如果 Fault Source 为 proxy,Fault Code 为 protocol.http.BadFormData,且 Fault Policy 不为空,则表示在 Fault Policy 中指示的特定政策读取或提取包含不允许 使用的任何字符的表单数据(表单参数)时发生错误。
  11. 在此示例中,X-Apigee-fault-policy 为 extractvariables/EV- ExtractFormParams, ,这意味着名为 EV-ExtractFormParams 的 ExtractVariables 政策在读取或提取表单 参数时失败。

Trace 工具

如需使用 Trace 工具诊断错误,请执行以下操作:

  1. 启用跟踪会话,然后执行以下任一操作:
    • 等待 500 Internal Server Error 错误发生,或
    • 如果您可以重现问题,请进行 API 调用以重现问题 500 Internal Server Error
  2. 确保已启用 Show all FlowInfos :

  3. 选择一个失败的请求,然后检查跟踪记录。
  4. 浏览跟踪记录的不同阶段,找到发生失败的位置 。
  5. 您通常会在其中一项政策中找到错误,如下所示:

    在上面的示例跟踪记录中,请注意,失败发生在 名为 EV-ExtractFormParams 的 ExtractVariables 政策中。

  6. 前往失败的特定政策之后名为 Error 的流:

  7. 记下跟踪记录中的以下值:

    error: Bad Form Data

    state: PROXY_REQ_FLOW

    error.class:: com.apigee.rest.framework.BadRequestException

    • 错误 Bad Form Data 的值表示表单 参数包含一些 不允许 使用的字符。
    • 状态 PROXY_REQ_FLOW, 的值表示 错误发生在 API 代理的请求流 中。
  8. 前往跟踪记录中的 AX (记录的分析数据)阶段,然后点击 该阶段。
  9. 向下滚动到 Phase Details - Error Headers 部分,然后确定 X-Apigee-fault-code、X-Apigee-fault-source 和 X-Apigee-fault-policy 的值,如下所示:

  10. 请注意,X-Apigee-fault-code 和 X-Apigee-fault-source 的值分别为 protocol.http.BadFormData 和 policy,且 X-Apigee-fault-policy 不为空。这表示在 X-Apigee-fault-policy 中指示的特定政策读取或提取包含不允许 使用的任何字符的表单数据(表单参数)时发生错误。

    响应标头 值
    X-Apigee-fault-code protocol.http.BadFormData
    X-Apigee-fault-source policy
    X-Apigee-fault-policy extractvariables/EV-ExtractFormParams
  11. 在此示例中,X-Apigee-fault-policy 为 extractvariables/EV- ExtractFormParams, ,这意味着名为 EV-ExtractFormParams 的 ExtractVariables 政策在读取或提取表单 参数时失败。

NGINX

如需使用 NGINX 访问日志诊断错误,请执行以下操作:

  1. 如果您是 Private Cloud 用户,则可以使用 NGINX 访问日志来 确定有关 HTTP 500 Internal Server Error 的关键信息。
  2. 检查 NGINX 访问日志:

    /opt/apigee/var/log/edge-router/nginx/ORG~ENV.PORT#_access_log

  3. 搜索以查看在特定时间段内(如果问题过去发生过)是否存在任何 500 错误,错误代码为 protocol.http.BadFormData,或者是否存在任何请求仍然失败并显示 500。
  4. 如果您确实发现任何 500 错误,且 X-Apigee-fault-code 与 protocol.http.BadFormData 的值匹配,请确定 X-Apigee-fault-source 和 X-Apigee-fault-policy 的值。

    NGINX 访问日志中的 500 错误示例:

    NGINX 访问日志中的上述示例条目具有以下 X-Apigee-fault-code 和 X-Apigee-fault-source: 值:

    标头 值
    X-Apigee-fault-code protocol.http.BadFormData
    X-Apigee-fault-source policy
    X-Apigee-fault-policy extractvariables/EV-ExtractFormParams
  5. 请注意,X-Apigee-fault-code 和 X-Apigee-fault-source 的值分别为 protocol.http.BadFormData 和 policy ,且 X-Apigee-fault-policy 不为空。这表示在 X-Apigee-fault-policy, 中指示的特定政策读取或提取包含不允许 使用的任何字符的表单数据(表单参数)时发生错误。
  6. 在此示例中,X-Apigee-fault-policy 为 extractvariables/EV- ExtractFormParams, ,这意味着名为 EV-ExtractFormParams 的 ExtractVariables 政策在读取表单 参数时失败。

原因:请求中的表单参数包含不允许使用的字符

诊断

  1. 使用 API 监控、Trace 工具或 NGINX 访问日志确定 Fault Code 、Fault Source 和 Fault Policy 500 Internal Server Error,如 常见诊断步骤中所述。
  2. 如果 Fault Code 为 protocol.http.BadFormData,Fault Source 的值为 proxy 或 policy,且 Fault Policy 不为空, 则表示 Fault Policy 中指定的政策在读取或提取表单数据(表单参数)时失败。
  3. 检查 Fault Policy 中指示的政策,并确定以下信息:
    1. Source: 确定政策是从 请求还是响应中读取或提取数据。
    2. 表单参数: 确定政策中正在读取的特定表单参数。

      示例 1

      示例 1:ExtractVariables 政策提取表单参数:

            <ExtractVariables name="EV-ExtractFormParms">
               <DisplayName>EV-ExtractFormParams</DisplayName>
               <Source>request</Source>
               <FormParam name="username">
                  <Pattern ignoreCase="false">{username}</Pattern>
               </FormParam>
               <FormParam name="password">
                 <Pattern ignoreCase="false">{password}</Pattern>
               </FormParam>
               <VariablePrefix>forminfo</VariablePrefix>
             <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
            </ExtractVariables>
            

      在上面的 ExtractVariables 政策中:

      • Source: request

        这由 <Source> 元素指示

      • 表单参数: username 和 password

        这由 <Pattern> 元素中的 <FormParam> 元素指示

      这表示客户端作为 HTTP 请求 的一部分传递给 Apigee Edge 的表单参数 username 和/或 password 包含 不允许 使用的字符。

      示例 2

      示例 2:AssignMessage 政策复制表单参数:

            <AssignMessage continueOnError="false" enabled="true" name="AM-CopyFormParams">
              <Copy source="request">
                <FormParams>
                  <FormParam name="username"/>
                  <FormParam name="password"/>
                </FormParams>
              </Copy>
              <AssignTo createNew="true" transport="http" type="request"/>
            </AssignMessage>
            

      在上面的 ExtractVariables 政策中:

      • Source: request

        这由 source 属性在 <Copy> 元素指示

      • 表单参数: username 和 password

        这由 name 属性在 <FormParam> 元素中指示

      这表示客户端作为 HTTP 请求 的一部分传递给 Apigee Edge 的表单参数 username 或 password 或两者都包含 不允许 使用的任何字符。

  4. 使用以下方法之一检查在第 步 3 中标识的表单参数中是否存在任何不允许 使用的字符:

    Trace 工具

    如需使用 Trace 工具进行验证,请执行以下操作:

    1. 如果您已捕获失败请求的跟踪记录(如 常见诊断步骤中所述),请选择一个 失败的请求。
    2. 如果您已确定包含任何字符 的表单参数是上述第 3 步中的 HTTP 请求的一部分,这些字符不允许使用,请执行以下操作:
      1. 前往 Request Received from Client 阶段。
      2. 向下滚动到 Phase Details 部分,然后查看 Request Content。

        ( 查看放大图片)

      3. 在上面的示例中,请注意,表单参数 password 包含百分号 (%)。
      4. 由于百分号 (%) 也用于 百分号编码特殊字符,因此不能在 表单数据中原样使用。
      5. 因此,Apigee Edge 会返回 500 Internal Server Error,并返回错误代码 protocol.http.BadFormData。

    实际请求

    如需使用实际请求进行验证,请执行以下操作:

    1. 如果您无权访问向目标服务器发出的实际请求, 请前往解决方案。
    2. 如果您有权访问向 Apigee Edge 发出的实际请求,请执行以下步骤: 以下步骤:
      1. 查看表单数据内容,看看其中是否包含任何字符,这些字符 不允许 使用,例如百分号 (%) 或百分号 (%) 后跟无效的 十六进制字符。

        示例 1

        示例请求 1:表单数据作为请求的一部分

        curl -X GET "https://HOSTALIAS/myproxy -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=123456abc123&client_secret=c23578%ZY"
        

        在此示例中,请注意,元素 client_secret 包含百分号 (%) 后跟 无效的十六进制字符 ZY。

        示例 2

        示例请求 2:在文件中传递的表单数据:

        curl -X GET "https://HOSTALIAS/myproxy -H "Content-Type: application/x-www-form-urlencoded" -d @form_data.xml
        

        form_data.xml 的内容:

        xml=<user><username>abc1234@google.com</username><password>qwerty12345!@#$%</password></user>

        在此示例中,请注意,元素 password 包含百分号 (%),该百分号不应 在表单数据中原样传递。

    3. 在上述两个示例中,作为 HTTP 请求的一部分发送给 Apigee Edge 的表单数据包含不允许使用的字符。
    4. 因此,Apigee Edge 会返回 500 Internal Server Error ,并返回错误代码 protocol.http.BadFormData。

分辨率

  1. 确保客户端作为 HTTP 请求的一部分发送的表单数据或参数 的键和值中的任何特殊字符始终按照 表单数据 - application/x-www-form-urlencoded中的说明进行编码。
  2. 对于上述讨论的示例,您可以按如下方式解决问题:

    示例 1

    示例 1:作为请求的一部分传递的表单数据:

    使用与特定字符的 ASCII 代码匹配的有效 十六进制字符。 例如,如果您要发送美元符号 ($),请使用 %24 ,如下所示:

    curl -X GET "https://HOSTALIAS/myproxy -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=123456abc123&client_secret=c23578%24"
    

    示例 2

    示例请求 2:在文件中传递的表单数据:

    curl -X GET "https://HOSTALIAS/myproxy -H "Content-Type: application/x-www-form-urlencoded" -d @form_data.xml
    

    form_data.xml 的内容:

    对百分号 (%) 使用 百分号编码,也就是说,修改文件以包含 %25 ,如下所示:

    xml=<user><username>abc1234@google.com</username><password>qwerty12345!!@#$%25</password></user>

规范

Apigee Edge 要求按照以下规范发送表单数据 :

规范
表单数据 - application/x-www-form-urlencoded

如果您仍然需要 Apigee 支持团队的任何帮助,请前往必须收集 诊断信息。

必须收集的诊断信息

如果按照上述说明操作后问题仍然存在,请收集以下 诊断信息,然后联系 Apigee Edge 支持团队:

如果您是 Public Cloud 用户,请提供以下信息:

  • 组织名称
  • 环境名称
  • API 代理名称
  • 用于重现 500 Internal Server Error(错误代码为 protocol.http.BadFormData)的完整curl命令
  • API 请求的跟踪文件

如果您是 Private Cloud 用户,请提供以下信息:

  • 针对失败请求观察到的完整错误消息
  • 环境名称
  • API 代理软件包
  • API 请求的跟踪文件
  • NGINX 访问日志

    /opt/apigee/var/log/edge-router/nginx/ORG~ENV.PORT#_access_log

    其中: ORG、ENV 和 PORT# 替换为 实际值。

  • 消息处理器系统日志

    /opt/apigee/var/log/edge-message-processor/logs/system.log

参考