Salesforce Apex调用Redshift Data API遇UnknownOperationException错误求助
问题背景
我们是一家咨询公司,正在Salesforce平台上实现调用Amazon Redshift Data API的Apex出站请求认证,最终目标是通过Salesforce的Valence应用执行SQL调用并拉取结果。目前提交带DescribeTable动作的POST请求时,仅收到XML格式的UnknownOperationException错误,需要请求构建的说明和基础示例来排查问题。
核心疑问
- POST请求中,
Action和Version是否应放在查询字符串而非请求体中? - 是否需要额外查询字符串参数来正确路由请求?
- 尽管URL有字符限制,是否应改用GET请求?
Content-Type请求头是否需刻意设置,且是否需纳入规范请求?
已尝试方案
- 使用Salesforce的命名/外部凭证,通过AWS Signature Version 4协议认证,但不清楚其与请求参数、请求体、请求头的交互方式;
- 自行编写认证协议代码,但两种方式均出现相同错误,无法定位问题。
已尝试GET和POST请求均无效,倾向使用POST请求。以下是脱敏后的测试代码及Postman接收的请求头信息:
测试Apex代码
global class JPTestClass { public static void jpTestMethod(){ String POSTMAN_CALLOUT = 'callout:Testing_Postman_API'; // 指向https://redshift-data.us-east-1.amazonaws.com String REDSHIFT_CALLOUT = 'callout:Redshift_Data_API'; send(POSTMAN_CALLOUT); send(REDSHIFT_CALLOUT); } public static void send(String callout){ HttpRequest request = new HttpRequest(); // POST请求体中的参数 String payloadString = JSON.serialize(new Map<String, Object>{ 'Action' => 'DescribeTable', 'Version' => '2010-05-08', 'ClusterIdentifier' => '<<<XXX>>>', 'ConnectedDatabase' => '<<<XXX>>>', 'Database' => '<<<XXX>>>', 'MaxResults' => 1000, 'NextToken' => '', 'Schema' => '<<<XXX>>>', 'SecretArn' => '<<<XXX>>>', 'Table' => 'salesforce_contacts', 'WorkgroupName' => '' }); System.debug(payloadString); request.setEndpoint(callout + '?Action=DescribeTable&Version=2010-05-08'); request.setMethod('POST'); request.setBody(payloadString); HttpResponse response = new Http().send(request); System.debug(response.getBody()); } }
Postman接收的请求头
x-forwarded-for: "xxxxxxxxx" x-forwarded-proto: "xxxxxxxxx" x-forwarded-port: "xxxxxxxxx" host: "36b0a2b5-79a5-471e-ac92-06122c76dad2.mock.pstmn.io" x-amzn-trace-id: "Root=1-639b9d8c-64700ff707de06582eafc6ce" content-length: "263" user-agent: "SFDC-Callout/56.0" sfdc_stack_depth: "1" accept: "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2" authorization: "AWS4-HMAC-SHA256 Credential=<<<ACCESS KEY>>>/20221215/us-east-1/redshift-data/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=0be75a778aa72003cd29140ea52b3db74b6d559078efa382697f8d78b5460b7f" x-amz-date: "20221215T221956Z" x-amz-content-sha256: "ec4ae9564eca8bf406807d8ec6305ddbf8435095b269db6f3162b5cfcac4cad8" accept-encoding: "gzip,deflate"
核心问题定位
UnknownOperationException的根源是Redshift Data API不接受JSON格式的POST请求体,且请求头未正确设置Content-Type,导致AWS无法识别请求中的操作类型。
疑问解答
Action和Version的位置:
可放在查询字符串或请求体中,但必须保持格式统一。如果用POST,建议统一放在application/x-www-form-urlencoded格式的请求体里;不要混合JSON参数和查询字符串参数,否则AWS无法正确解析。额外查询字符串参数:
不需要,Redshift Data API仅需正确的服务端点(如https://redshift-data.us-east-1.amazonaws.com),加上Action和Version即可完成路由。是否改用GET请求:
不建议,GET的URL长度限制会影响复杂请求(如长SQL语句),POST更适合这类场景,只要格式正确就能正常工作。Content-Type请求头的设置:
必须设置为application/x-www-form-urlencoded,且该头必须纳入AWS SigV4的签名计算(即Salesforce外部凭证的签名Headers列表需包含content-type)。这是解决报错的关键步骤之一。
修正后的Apex代码示例
global class JPTestClass { public static void jpTestMethod(){ String REDSHIFT_CALLOUT = 'callout:Redshift_Data_API'; send(REDSHIFT_CALLOUT); } public static void send(String callout){ HttpRequest request = new HttpRequest(); // 构造键值对参数,所有值必须为字符串类型 Map<String, String> params = new Map<String, String>{ 'Action' => 'DescribeTable', 'Version' => '2010-05-08', 'ClusterIdentifier' => '<<<XXX>>>', 'ConnectedDatabase' => '<<<XXX>>>', 'Database' => '<<<XXX>>>', 'MaxResults' => '1000', 'NextToken' => '', 'Schema' => '<<<XXX>>>', 'SecretArn' => '<<<XXX>>>', 'Table' => 'salesforce_contacts', 'WorkgroupName' => '' }; // 转换为x-www-form-urlencoded格式的请求体 String payload = ''; for(String key : params.keySet()){ if(payload != '') payload += '&'; payload += EncodingUtil.urlEncode(key, 'UTF-8') + '=' + EncodingUtil.urlEncode(params.get(key), 'UTF-8'); } request.setEndpoint(callout); // 无需在URL追加Action和Version,已包含在请求体 request.setMethod('POST'); request.setHeader('Content-Type', 'application/x-www-form-urlencoded'); request.setBody(payload); HttpResponse response = new Http().send(request); System.debug(response.getBody()); } }
Salesforce外部凭证配置注意事项
- 签名版本选择AWS Signature Version 4;
- 签名Headers必须包含
content-type、host、x-amz-date; - 服务名称填写
redshift-data,区域填写对应的AWS区域(如us-east-1); - 确保关联的命名凭证拥有足够的AWS权限(如
redshift-data:DescribeTable)。
内容的提问来源于stack exchange,提问作者Beastmanchild

