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 CodeTime 的关系图。

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

    查看放大图片

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

    查看放大图片

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

  9. Logs 窗口中,记下以下详细信息:
    • Status Code500
    • Fault Sourceproxy
    • Fault Codeprotocol.http.BadFormData
    • Fault Policyextractvariables/EV-ExtractFormParams
  10. 如果 Fault SourceproxyFault Codeprotocol.http.BadFormData,且 Fault Policy 不为空,则表示在 Fault Policy 中指示的特定政策读取或提取包含不允许 使用的任何字符的表单数据(表单参数)时发生错误。
  11. 在此示例中,X-Apigee-fault-policyextractvariables/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-codeX-Apigee-fault-sourceX-Apigee-fault-policy 的值,如下所示:

  10. 请注意,X-Apigee-fault-codeX-Apigee-fault-source 的值分别为 protocol.http.BadFormDatapolicy,且 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-policyextractvariables/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-codeprotocol.http.BadFormData 的值匹配,请确定 X-Apigee-fault-sourceX-Apigee-fault-policy 的值。

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

    NGINX 访问日志中的上述示例条目具有以下 X-Apigee-fault-codeX-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-codeX-Apigee-fault-source 的值分别为 protocol.http.BadFormDatapolicy ,且 X-Apigee-fault-policy 不为空。这表示在 X-Apigee-fault-policy, 中指示的特定政策读取或提取包含不允许 使用的任何字符的表单数据(表单参数)时发生错误。
  6. 在此示例中,X-Apigee-fault-policyextractvariables/EV- ExtractFormParams, ,这意味着名为 EV-ExtractFormParams 的 ExtractVariables 政策在读取表单 参数时失败。

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

诊断

  1. 使用 API 监控、Trace 工具或 NGINX 访问日志确定 Fault CodeFault SourceFault Policy 500 Internal Server Error,如 常见诊断步骤中所述。
  2. 如果 Fault Codeprotocol.http.BadFormDataFault Source 的值为 proxypolicy,且 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> 元素指示

      • 表单参数usernamepassword

        这由 <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 政策中:

      • Sourcerequest

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

      • 表单参数usernamepassword

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

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

  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

    其中ORGENVPORT# 替换为 实际值。

  • 消息处理器系统日志

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

参考