我正在尝试通过 Salesforce 与 REST API 进行通信。 我有一个邮递员集合,工作得很好,但是当尝试使用 apex 复制它时,我收到状态 400(错误请求)错误。
URL 已在远程站点设置中注册。 这是我的代码(我不允许发布参数,但相信我它们与邮递员集合相同):
public static void testCode(){
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('<API URL>?script=1022&deploy=1');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json');
String nonce = String.valueOf(Crypto.getRandomLong());
String timestamp = String.valueOf(DateTime.now().getTime() / 1000);
String consumerSecret = '<SECRET>';
String header = 'OAuth ';
header+= 'realm="8305111_SB1", ';
header+= 'oauth_signature_method="HMAC-SHA1"';
header+= 'oauth_consumer_key="<KEY>", ';
header+= 'oauth_token="<TOKEN>", ';
header+= 'oauth_token_secret="<TOKEN>", ';
header+= 'oauth_version="1.0", ';
header+= 'oauth_timestamp="'+timestamp+'", ';
header+= 'oauth_nonce="'+nonce+'", ';
Map<String,String> parameters = new Map<String,String>();
parameters.put('realm','8305111_SB1');
parameters.put('oauth_signature_method','HMAC-SHA1');
parameters.put('oauth_consumer_key','<KEY>');
parameters.put('oauth_token','<TOKEN>');
parameters.put('oauth_token_secret','<SECRET>');
parameters.put('oauth_version','1.0');
parameters.put('oauth_timestamp',timestamp);
parameters.put('oauth_nonce',nonce);
header += 'oauth_signature="'+generateSignature(request, consumerSecret, parameters)+'"';
request.setHeader('Authorization', header);
request.setBody('{"Mundo": "Mundo"}');
System.Debug('AUTH HEADER:');
for(String s : header.Split(',')){System.Debug(s);}
System.Debug('BODY: '+request.getBody());
System.debug('REQUEST --> '+request);
System.debug('RESPONSE --> '+http.send(request));
}
private static String generateSignature(HttpRequest req, String consumerSecret, Map<String,String> parameters) {
String s = createBaseString(parameters,req);
Blob sig = Crypto.generateMac( 'HmacSHA1', Blob.valueOf(s), Blob.valueOf(consumerSecret) );
return EncodingUtil.urlEncode(EncodingUtil.base64encode(sig), 'UTF-8');
}
private static String createBaseString(Map<String,String> oauthParams, HttpRequest req) {
Map<String,String> p = oauthParams.clone();
if(req.getMethod().equalsIgnoreCase('post') && req.getBody()!=null &&
req.getHeader('Content-Type')=='application/x-www-form-urlencoded') {
p.putAll(getUrlParams(req.getBody()));
}
String host = req.getEndpoint();
Integer n = host.indexOf('?');
if(n>-1) {
p.putAll(getUrlParams(host.substring(n+1)));
host = host.substring(0,n);
}
List<String> keys = new List<String>();
keys.addAll(p.keySet());
keys.sort();
String s = keys.get(0)+'='+p.get(keys.get(0));
for(Integer i=1;i<keys.size();i++) {
s = s + '&' + keys.get(i)+'='+p.get(keys.get(i));
}
// According to OAuth spec, host string should be lowercased, but Google and LinkedIn
// both expect that case is preserved.
return req.getMethod().toUpperCase()+ '&' + EncodingUtil.urlEncode(host, 'UTF-8') + '&' +
EncodingUtil.urlEncode(s, 'UTF-8');
}
private static Map<String,String> getUrlParams(String value) {
Map<String,String> res = new Map<String,String>();
if(value==null || value=='') {
return res;
}
for(String s : value.split('&')) {
System.debug('getUrlParams: '+s);
List<String> kv = s.split('=');
if(kv.size()>1) {
// RFC 5849 section 3.4.1.3.1 and 3.4.1.3.2 specify that parameter names
// and values are decoded then encoded before being sorted and concatenated
// Section 3.6 specifies that space must be encoded as %20 and not +
String encName = EncodingUtil.urlEncode(EncodingUtil.urlDecode(kv[0],
'UTF-8'), 'UTF-8').replace('+','%20');
String encValue = EncodingUtil.urlEncode(EncodingUtil.urlDecode(kv[1], 'UTF-8'),
'UTF-8').replace('+','%20');
//System.debug('getUrlParams: -> '+encName+','+encValue);
res.put(encName,encValue);
}
}
return res;
}
如果它不是一个公共 API,你会遇到困难,互联网上的陌生人无法复制你正在做的事情;)考虑将这些请求发送到某些服务,如 requestbin、httpbin 等(或者也许你有自己的网络服务器,你可以仅用于记录请求...我认为其中一些是付费的,继续谷歌搜索)。比较从 apex 发出的内容与邮递员发送的内容?
假设它们确实相同 - 供应商可能正在做一些 IP 白名单吗?也许他们需要将 Salesforce IP 地址列入白名单(或者他们可能禁止 AWS,而你在 hyperforce 组织中)