I tried to see the request header values before it is send to external webserver. But Httprequest.getbody() methods returns empty. I tried this code below. Also, I have tried to convert the java code which has been given as an example for the POST request. Please see the java code also given below the apex code.
public with sharing class testingscenario
{
public static String generateAuthHeader(String dataToSign)
{
String algorithmName = 'HmacSHA256';
Blob hmacData = Crypto.generateMac(algorithmName, Blob.valueOf(dataToSign), Blob.valueOf('**********************************'));
return EncodingUtil.base64Encode(hmacData);
}
@Future(callout = true)
public static void getvalues(String JsonString)
{
try {
String gatewayurl = '/v1/';
String gatewayhost = 'somewebsite.com';
String apikey = '**************************';
String apisecret = '**************************************';
String contentType = 'application/json';
String ND=Datetime.now().formatGMT('EEE, dd MMM yyyy HH:mm:ss z');
//String jsonBody = '{\"secondaryFields\":[],\"entityType\":\"INDIVIDUAL\",\"customFields\":[],\"groupId\":\"418f28a7-b9c9-4ae4-8530-819c61b1ca6c\",\"providerTypes\":[\"WATCHLIST\"],\"name\":\"george w bush\"}';
String jsonBody = '{ \"secondaryFields\": [], \"entityType": \"INDIVIDUAL\", \"customFields\": [], "groupId\":\"*********************\", \"providerTypes\": [ \"WATCHLIST\" ], \"name\": \"putin\" }';
JSONGenerator gen = JSON.createGenerator(true);
List<String> str1 = new List<String>();
List<String> str2 = new List<String>();
str2.add('WATCHLIST');
// Write data to the JSON string.
gen.writeStartObject();
gen.writeObjectField('secondaryFields', str1);
gen.writeStringField('entityType', 'INDIVIDUAL');
gen.writeObjectField('customFields',str1);
gen.writeStringField('groupId', '***********************');
gen.writeObjectField('ProviderTypes', str2);
gen.writeStringField('name', 'george');
gen.writeEndObject();
// Get the JSON string.
String pretty = gen.getAsString();
System.debug('json string:>>>>>>>>>'+pretty );
System.debug('json body:>>>>>>>>>'+jsonbody);
String content = JSON.serialize(jsonbody);
Integer Contentlength=192; //pretty.length();
System.debug('Contentlengthhhhhh'+Contentlength);
/*
String dataToSign = '(request-target): post' + gatewayurl+ 'cases\n' +
'host: ' + gatewayhost + '\n' +
'date: ' + ND +'\n'+
'content-type: '+contentType +'\n' +
'content-length: '+ Contentlength + '\n' +
content;
*/
String dataToSign2 = '(request-target): post ' + gatewayurl + 'cases\n' +
'host: ' + gatewayhost + '\n' +
'date: ' + ND + '\n' +
'content-type: ' + contentType +'\n' +
'content-length: ' + contentLength + '\n' +
content;
String dataToSign1 = '(request-target): post /v1/cases host: somewebsite.com date: Fri, 22 Sep 2017 22:13:50 GMT content-type: application/json content-length: 192 { \"secondaryFields\": [], \"entityType\": \"INDIVIDUAL\", \"customFields\": [], \"groupId\":\"************************\", \"providerTypes\": [ \"WATCHLIST\" ], \"name\": \"putin\" }';
String hmac = generateAuthHeader(dataToSign1);
String authorisation = 'Signature keyId=\"' + apikey + '\",algorithm=\"hmac-sha256\",headers=\"(request-target) host date content-type content-length\",signature=\"' + hmac + '\"';
//String authorisation = 'Signature keyId=\"**********************\",algorithm=\"hmac-sha256\",headers=\"(request-target) host date content-type content-length\",signature=\"***********************"';
System.debug('authorisation Result'+authorisation);
System.debug('dataToSign Result'+dataToSign1 );
System.debug('hmac Result'+hmac);
HttpRequest request = new HttpRequest();
request.setEndpoint('https://somewebsite.com/v1/cases');
request.setMethod('POST');
request.setTimeout(120000);
request.setHeader('Authorization',authorisation);
request.setHeader('Date',ND);
request.setHeader('Cache-Control', 'no-cache');
request.setHeader('Content-Type', 'application/json');
request.setHeader('Content-Length', String.valueOf(contentlength));
System.debug('request body>>>>>>'+request.getBody());
System.debug('request body Document>>>>>>'+request.getBodyDocument());
Http http = new Http();
HTTPResponse res = http.send(request);
System.debug('response>>>>>>'+res);
} catch(exception ex) {
system.debug(ex.getmessage());
}
}
}
Java Code for POST Request:
import java.util.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.fasterxml.jackson.databind.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
//import org.apache.commons.logging.*;
import org.json.*;
public class PostRequest {
public static String generateAuthHeader(String dataToSign, String secret)
{
String hash = "";
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
hash = Base64.encodeBase64String(sha256_HMAC.doFinal(dataToSign.getBytes()));
}
catch (Exception e){
System.out.println("Error");
}
return(hash);
}
// A simple request to create a Case using Http Post
public static void main(String[] args) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
Date now = new Date();
//format for date string Mon, 27 Mar 2017 15:19:36 GMT
DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
df.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = df.format(now);
String gatewayurl = "/v1/";
String gatewayhost = "someapiwebsite.com";
String apikey = "**************************************";
String apisecret = "**************************************";
String groupid = "**************************************";
//create a JSON string
String jsonBody = "{\"secondaryFields\":[],\"entityType\":\"INDIVIDUAL\",\"customFields\":[],\"groupId\":\"**************************************\",\"providerTypes\":[\"WATCHLIST\"],\"name\":\"george w bush\"}";
// create a JSON object from the JSON string
JSONObject jo = new JSONObject(jsonBody);
//System.out.println(jo.toString());
String jlen = String.valueOf(jo.toString().length());
String dataToSign = "(request-target): post " + gatewayurl + "cases\n" +
"host: " + gatewayhost + "\n" +
"date: " + date + "\n" +
"content-type: " + "application/json" +"\n" +
"content-length: " + jlen + "\n" +
jo;
String hmac = generateAuthHeader(dataToSign, apisecret);
String authorisation = "Signature keyId=\"" + apikey + "\",algorithm=\"hmac-sha256\",headers=\"(request-target) host date content-type content-length\",signature=\"" + hmac + "\"";
System.out.println(jlen);
System.out.println(dataToSign);
//System.out.println(hmac);
System.out.println(authorisation);
String msg = jo.toString();
HttpPost httpPost = new HttpPost("https://someapiwebsite.com/v1/cases");
HttpEntity entity = new StringEntity(msg);
httpPost.setEntity(entity);
httpPost.addHeader("Date", date);
httpPost.addHeader("Cache-Control", "no-cache");
httpPost.addHeader("Content-Type", "application/json" );
httpPost.addHeader("Authorization", authorisation);
// send the POST request
CloseableHttpResponse response1 = httpclient.execute(httpPost);
try {
HttpEntity entity1 = response1.getEntity();
System.out.println(response1.getStatusLine());
String json = EntityUtils.toString(response1.getEntity());
//System.out.println(entity1);
// json string returned
System.out.println(json);
ObjectMapper mapper = new ObjectMapper();
// printout in Pretty format
Object jsonObj = mapper.readValue(json, Object.class);
String indented = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObj);
System.out.println(indented);
EntityUtils.consume(entity1);
}
finally {
response1.close();
}
} finally {
httpclient.close();
}
}
}
Best Answer
If you don't control the target website and can't access logs there - you could try sending the request to a debug logging endpoint. I tend to do it when I'm building new SOAP / REST integrations. When I hit a problem, I debug log the resulting request Salesforce has produced, load it up in soapUI / Chrome REST client and keep hacking at the generated message until I get something meaningful from the target system...
If you don't have your own "request sink" that would log anything you want, you can use https://requestb.in/ It's very simple to use and for your code it means only 2 things:
request.setEndpoint()
As to what's actually wrong - can be anything. Maybe your content-type header is not something the target application expects. Maybe the message signing algorithm is not implemented the way they expect. That's why it's a good idea to send your stuff to such "request sink" and when all else fails - contact target app's developer for assistance, showing him/her the raw output produced by SF.
(No, I'm not affiliated with request bin, soapUI and any other tool like that)
Edit
Here's a working Salesforce implementation of Thomson Reuter's World-Check One API based on their sample Java program.
You need to provide your own API keys. Successful request will receive a response with HTTP 201 "Created" and JSON payload starting with something like