500 内部服务器错误 - BadFormData

您正在查看的是 Apigee Edge 文档。
转到 Apigee X 文档
信息

问题

客户端应用收到 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. 内容类型:application/x-www-form-urlEncoding
    • 如果表单数据很小,则数据以键值对的形式发送,并且:
      • 按照 表单 - 第 17.13.4.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 或 AllocationMessage 政策读取包含不允许在请求流中使用的任何字符的特定表单参数。

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

    以下是导致此错误的可能原因:

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

常见诊断步骤

使用以下工具/技巧之一来诊断此错误:

API 监控

要使用 API Monitoring 诊断错误,请执行以下操作:

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

  3. 前往 Analyze > API Monitoring > Investigate 页面。
  4. 选择您发现错误的具体时间范围。
  5. 根据时间绘制故障代码

  6. 选择具有错误代码 protocol.http.BadFormData 的单元格,如下所示:

    查看放大图片

  7. 错误代码 protocol.http.BadFormData 的相关信息如下所示:

    查看放大图片

  8. 点击查看日志,然后展开失败请求所在的行。

  9. 日志窗口中,请注意以下详细信息:
    • 状态代码500
    • 错误来源proxy
    • 错误代码protocol.http.BadFormData
    • 错误政策extractvariables/EV-ExtractFormParams
  10. 如果故障来源proxy故障代码protocol.http.BadFormData故障政策为非空,则表示故障政策中指示的特定政策正在读取或提取包含不允许使用的任何字符的表单数据(表单参数)时发生错误。
  11. 在此示例中,X-Apigee-fault-policyextractvariables/EV- ExtractFormParams, ,这意味着名为 EV-ExtractFormParams 的 ExtractVariables 政策在读取或提取表单参数时失败。

跟踪工具

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

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

  3. 选择其中一个失败的请求并检查跟踪记录。
  4. 浏览跟踪记录的不同阶段,并找到发生故障的位置。
  5. 您通常可以在下列某项政策中找到该错误:

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

  6. 找到失败的特定政策后名为错误的流程:

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

    错误:Bad Form Data

    状态:PROXY_REQ_FLOW

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

    • 错误 Bad Form Data 的值表示表单参数包含一些不允许使用的字符。
    • 状态 PROXY_REQ_FLOW, 的值表示 API 代理的请求流程中发生了错误。
  8. 进入跟踪记录中的 AX(已记录 Google Analytics(分析)数据)阶段并点击该阶段。
  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.BadFormDatapolicyX-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. 搜索以查看在特定持续时间内是否存在任何错误代码为 protocol.http.BadFormData500 错误(如果问题是在过去发生的),或者是否有任何请求仍然失败并显示 500
  4. 如果您确实发现 X-Apigee-fault-codeprotocol.http.BadFormData 的值匹配的任何 500 错误,请确定 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.BadFormData policy ,而 X-Apigee-fault-policy 非空。这表示错误发生在 X-Apigee-fault-policy, 中指示的特定政策,读取或提取表单数据(表单参数)时,这些数据包含任何不允许使用X-Apigee-fault-policy,的字符。
  6. 在此示例中,X-Apigee-fault-policyextractvariables/EV- ExtractFormParams, ,这意味着名为 EV-ExtractFormParams 的 ExtractVariables 政策在读取表单参数时失败。

原因:请求中的表单参数包含禁用字符

诊断

  1. 按照常见诊断步骤中的说明,使用 API 监控、跟踪工具或 NGINX 访问日志确定 500 Internal Server Error故障代码故障来源故障政策
  2. 如果故障代码protocol.http.BadFormData故障来源的值为 proxypolicy,且故障政策不为空,则表示故障政策中指定的政策在读取或提取表单数据(表单参数)时失败。
  3. 检查故障政策中指明的政策并确定以下信息:
    1. 来源:确定政策是读取请求或响应中的数据还是从中提取数据。
    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 政策中:

      • 来源:request

        这由 <Source> 元素指明

      • 表单参数usernamepassword

        这由 <FormParam> 元素内的 <Pattern> 元素指明

      这表示作为客户端的 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 政策中:

      • 来源request

        这由 <Copy> 元素中的 source 属性指明

      • 表单参数usernamepassword

        这由 <FormParam> 元素中的 name 属性指明

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

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

    跟踪工具

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

    1. 如果您已按照常见诊断步骤中所述捕获了失败请求的跟踪记录,请选择其中一个失败请求。
    2. 如果您已确定包含不允许使用任何字符的表单参数已包含在上述第 3 步的 HTTP 请求中,则:
      1. 进入 Request Received from Client 阶段。
      2. 向下滚动到阶段详情部分,然后查看请求内容

        查看大图

      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-urlcoding 中所述进行编码。
  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-urlEncoding

如果您仍然需要 Apigee 支持的任何帮助,请参阅必须收集诊断信息

必须收集的诊断信息

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

如果您是公有云用户,请提供以下信息:

  • 组织名称
  • 环境名称
  • API 代理名称
  • 完成用于重现 500 Internal Server Error 且包含错误代码 protocol.http.BadFormDatacurl 命令
  • 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

参考