您正在查看 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 请求或响应的一部分。
表单数据传输
- Content-Type:application/x-www-form-urlencoded
- 如果表单数据的大小较小,则数据将作为键值对发送,其中:
- 两个键中的字符均按照 表单 - 第 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 代码。
- 如果表单数据的大小较小,则数据将作为键值对发送,其中:
- Content-Type:multipart/form-data
如果您要传输大量二进制数据或包含非 ASCII 字符的文本,则可以使用
Content-Type:multipart/form-data 发送数据,如 表单 - 第 17.13.4.2 节中所述
可能的原因
当且仅当满足以下所有条件时,会出现此错误:
- 客户端向 Apigee Edge 发送的 HTTP 请求 包含:
Content-Type: application/x-www-form-urlencoded,以及- 带有百分号 (
%) 或百分号 (%) 后跟无效的十六进制字符的表单数据,这些字符根据 表单 - 第 17.13.4.1 节是不允许的。
Apigee Edge 中的 API 代理会读取特定表单参数,其中包含请求流中使用 ExtractVariables 或 AssignMessage 政策 不允许 使用的任何字符。
例如,如果表单数据包含百分号 (
%) 原样(不编码)或百分号 (%) 后跟键和/或值中的任何无效十六进制字符,则会收到此错误。以下是造成此错误的可能原因:
原因 说明 适用的问题排查说明 请求中的表单参数包含不允许使用的字符 客户端作为 HTTP 请求的一部分传递的表单参数包含任何 不允许使用的字符。 Edge Public 和 Private Cloud 用户
常见诊断步骤
请使用以下工具/方法之一来诊断此错误:
API 监控
如需使用 API 监控诊断错误,请执行以下操作:
- 以具有 适当角色的用户身份登录 Apigee Edge 界面。
切换到您要调查问题的组织。
- 依次前往 Analyze > API Monitoring > Investigate 页面。
- 选择您观察到错误的具体时间范围。
绘制 Fault Code 与 Time 的关系图。
选择包含故障代码
protocol.http.BadFormData的单元格,如下所示:(查看放大图片)
系统会显示有关故障代码
protocol.http.BadFormData的信息,如下所示:(查看放大图片)
点击 View logs ,然后展开失败请求的行。
- 在 Logs 窗口中,记下以下详细信息:
- Status Code:
500 - Fault Source:
proxy - Fault Code:
protocol.http.BadFormData - Fault Policy:
extractvariables/EV-ExtractFormParams
- Status Code:
- 如果 Fault Source 为
proxy,Fault Code 为protocol.http.BadFormData,且 Fault Policy 不为空,则表示在 Fault Policy 中指示的特定政策读取或提取包含不允许 使用的任何字符的表单数据(表单参数)时发生错误。 - 在此示例中,X-Apigee-fault-policy 为
extractvariables/EV- ExtractFormParams,,这意味着名为 EV-ExtractFormParams 的 ExtractVariables 政策在读取或提取表单 参数时失败。
Trace 工具
如需使用 Trace 工具诊断错误,请执行以下操作:
- 启用跟踪会话,然后执行以下任一操作:
- 等待
500 Internal Server Error错误发生,或 - 如果您可以重现问题,请进行 API 调用以重现问题
500 Internal Server Error
- 等待
确保已启用 Show all FlowInfos :
- 选择一个失败的请求,然后检查跟踪记录。
- 浏览跟踪记录的不同阶段,找到发生失败的位置 。
您通常会在其中一项政策中找到错误,如下所示:
在上面的示例跟踪记录中,请注意,失败发生在 名为
EV-ExtractFormParams的 ExtractVariables 政策中。前往失败的特定政策之后名为 Error 的流:
- 记下跟踪记录中的以下值:
error:
Bad Form Datastate:
PROXY_REQ_FLOWerror.class::
com.apigee.rest.framework.BadRequestException- 错误
Bad Form Data的值表示表单 参数包含一些 不允许 使用的字符。 - 状态
PROXY_REQ_FLOW,的值表示 错误发生在 API 代理的请求流 中。
- 错误
- 前往跟踪记录中的 AX (记录的分析数据)阶段,然后点击 该阶段。
向下滚动到 Phase Details - Error Headers 部分,然后确定 X-Apigee-fault-code、X-Apigee-fault-source 和 X-Apigee-fault-policy 的值,如下所示:
请注意,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.BadFormDataX-Apigee-fault-source policyX-Apigee-fault-policy extractvariables/EV-ExtractFormParams- 在此示例中,X-Apigee-fault-policy 为
extractvariables/EV- ExtractFormParams,,这意味着名为EV-ExtractFormParams的 ExtractVariables 政策在读取或提取表单 参数时失败。
NGINX
如需使用 NGINX 访问日志诊断错误,请执行以下操作:
- 如果您是 Private Cloud 用户,则可以使用 NGINX 访问日志来
确定有关 HTTP
500 Internal Server Error的关键信息。 检查 NGINX 访问日志:
/opt/apigee/var/log/edge-router/nginx/ORG~ENV.PORT#_access_log- 搜索以查看在特定时间段内(如果问题过去发生过)是否存在任何
500错误,错误代码为protocol.http.BadFormData,或者是否存在任何请求仍然失败并显示500。 如果您确实发现任何
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.BadFormDataX-Apigee-fault-source policyX-Apigee-fault-policy extractvariables/EV-ExtractFormParams- 请注意,X-Apigee-fault-code 和 X-Apigee-fault-source
的值分别为
protocol.http.BadFormData和policy,且 X-Apigee-fault-policy 不为空。这表示在 X-Apigee-fault-policy, 中指示的特定政策读取或提取包含不允许 使用的任何字符的表单数据(表单参数)时发生错误。 - 在此示例中,X-Apigee-fault-policy 为
extractvariables/EV- ExtractFormParams,,这意味着名为EV-ExtractFormParams的 ExtractVariables 政策在读取表单 参数时失败。
原因:请求中的表单参数包含不允许使用的字符
诊断
- 使用 API 监控、Trace 工具或 NGINX 访问日志确定 Fault Code 、Fault Source 和 Fault Policy
500 Internal Server Error,如 常见诊断步骤中所述。 - 如果 Fault Code 为
protocol.http.BadFormData,Fault Source 的值为proxy或policy,且 Fault Policy 不为空, 则表示 Fault Policy 中指定的政策在读取或提取表单数据(表单参数)时失败。 - 检查 Fault Policy 中指示的政策,并确定以下信息:
- Source: 确定政策是从 请求还是响应中读取或提取数据。
- 表单参数: 确定政策中正在读取的特定表单参数。
示例 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或两者都包含 不允许 使用的任何字符。
使用以下方法之一检查在第 步 3 中标识的表单参数中是否存在任何不允许 使用的字符:
Trace 工具
如需使用 Trace 工具进行验证,请执行以下操作:
实际请求
如需使用实际请求进行验证,请执行以下操作:
- 如果您无权访问向目标服务器发出的实际请求, 请前往解决方案。
- 如果您有权访问向 Apigee Edge 发出的实际请求,请执行以下步骤:
以下步骤:
- 查看表单数据内容,看看其中是否包含任何字符,这些字符
不允许 使用,例如百分号 (
%) 或百分号 (%) 后跟无效的 十六进制字符。示例 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包含百分号 (%),该百分号不应 在表单数据中原样传递。
- 查看表单数据内容,看看其中是否包含任何字符,这些字符
不允许 使用,例如百分号 (
- 在上述两个示例中,作为 HTTP 请求的一部分发送给 Apigee Edge 的表单数据包含不允许使用的字符。
- 因此,Apigee Edge 会返回
500 Internal Server Error,并返回错误代码protocol.http.BadFormData。
分辨率
- 确保客户端作为 HTTP 请求的一部分发送的表单数据或参数 的键和值中的任何特殊字符始终按照 表单数据 - application/x-www-form-urlencoded中的说明进行编码。
- 对于上述讨论的示例,您可以按如下方式解决问题:
示例 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