Salesforce REST API using Spring Web Services
There are many examples out there of using the Salesforce REST API. But I didn't come across any showing explicitly how to do it with Spring Web Services. Base Java, other Java libraries, Node.js, Ruby, curl, and so on are some of the examples you trip over. But not much with Spring Web Services. Though maybe it is out there and I missed. (The internet is infinite after all, right?) Thus, I coded up a quick example of how to do it. My goal wasn't to create another API library. My goal is to show how to. The codebase is at https://github.com/billryoung/sfdc-springws-example, but I'll walk through the key parts in this article.
Login
Everything starts with login. For my example, I coded the user/password authentication flow. For reference, the API docs for user/password login are here.
As the API docs show, the login needs a body with the user, password, and other attributes as a URL type string in the body. An easy way is to use spring's MultiValueMap. By default it will convert into that format and handle all string escaping. You do have special characters in your password, right? ;)
MultiValueMap<String,String> bodyValues = new LinkedMultiValueMap<String,String>();
bodyValues.add("grant_type", grant_type);
bodyValues.add("client_id", client_id);
bodyValues.add("client_secret", client_secret);
bodyValues.add("username", username);
bodyValues.add("password", password);
There isn't a body required for this operation, but the HTTP header must set a content type. And since we're sending a body and headers, we need to wrap both together into a HttpEntity.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String,String>> requestEntity = new HttpEntity<MultiValueMap<String,String>>(bodyValues, headers);
Before we can make the call, we need an object to save the result. We could save to a String to get the raw JSON, but we can make a simple Java object with get/set methods for each of the attributes that login call returns, like "access_token." There's nothing very fancy about this. See my example here.
Now we need a RestTemplate and then we can make a POST.
RestTemplate restTemplate = new RestTemplate();
String login_url = "https://login.salesforce.com/services/oauth2/token";
ResponseEntity<LoginResponse> responseEntity = restTemplate.exchange(
login_url, //where to send to
HttpMethod.POST, //POST for login
requestEntity, //what to send (body and headers)
LoginResponse.class); //what to parse the response into
Notes on the call.
- Change login url to test if needed
- postForEntity would work here too, but I'm showing exchange as it is a bit more general and that's the whole point of my example.
- Exchange has an optional last optional parameter that can replace variables in the URL we are posting too.
- responseEntity.getBody() has your LoginResponse which has your access_token and instance_url.
See the full code here
Query Example
Now that you've logged in, you have an access token and instance url. Other calls are similar in how to use the exchange method. I'll show how to do a Query of Account as an example. And for reference, the query API docs are here.
First, we need a few plain Java objects with get/set methods for everything that gets returned. Notice from the API that there is an outer entity that has records and each record has the attributes you queried for plus an embedded attributes. See my AccountQueryRecord, AccountQueryRecords, and QueryAttributes classes here.
The only special gotcha that I noticed is that your get/set method names must match up to the fields coming back, exactly including case. That makes it a bit odd when the API gives back upper case. For instance, a Java method like "getName" is a property of lower case "name." But there's an easy annotation to use.
import com.fasterxml.jackson.annotation.JsonProperty;
...
@JsonProperty("Name")
public String getName() {
return name;
}
@JsonProperty("Name")
public void setName(String name) {
this.name = name;
}
You can use the same annotation if you'd like just a better Java type name. (And be careful on the JsonProperty package. There are differently libraries out there. Make sure you're using the one spring is looking for.)
As for the actual API call, we need to create a dynamic URL. It needs the instance URL and the actual query. (Tip, springws has support for replacing and escaping variables into the URL.) And we need to supply the access token in header. Thus, we use the HttpEntity again. But this time, there isn't a body, so we don't have to worry about a body. The overall code goes like this:
String query = "select Id, Name from Account limit 10";
//create the url to call
String url = loginResponse.getInstance_url()
+"/services/data/"
+ "v"+api_version
+ "/query/?q={q}";
log.debug("query: url="+url);
//create a Map for variables in the URL
//namely the query
Map<String, String> uriVariables = new HashMap<String, String>();
uriVariables.put("q", query);
//create the headers - need to pass access token from login
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer "+loginResponse.getAccess_token());
//create our response
//body is empty string, since there is no body for this request
HttpEntity<String> requestEntity = new HttpEntity<String>("", headers);
//need a template to request things from
RestTemplate restTemplate = new RestTemplate();
//issue the GET
ResponseEntity<AccountQueryRecords.class> responseEntity = restTemplate.exchange(
url, //url to request
HttpMethod.GET, //GET operation
requestEntity, //request empty body and http headers (access token)
AccountQueryRecords.class, //class type to parse response into
uriVariables); //variables to replace in the url
For query API specifically, remember to check if the response has more records and do a follow up query. Also, the result class needs to match up with the specific fields you're selecting.
You can see all the code together in a full example here: My full codebase example
Overall, from there, the other operations are similar. If you look at how I did login and query, from there you could follow the same way to call any of the other API operations. I'm sure you'll need some other parts of the Spring WS API, but hopefully with these basics you can apply the tons of springws articles elsewhere on the web to build off it more.
Related links that may be helpful: