In the past I’ve complained about the Salesforce SOAP API for reading/writing your CRM databases. Last year Salesforce added a new API, one founded on RESTful principals, for developers to work with. I wish it was implemented as an OData service, but I guess the Salesforce guys are stuck behind the times.
At any rate, I had a need to read/write to a Salesforce instance and couldn’t bear to use their SOAP API again so I decided to give the REST API a go. Their developer site doesn’t do that much in the way of help for .NET developers (there is some, but not much)… everything is really skewed towards PHP or their own APEX language. After some searching I found Dan Boris’ series of Salesforce related posts. These are good and he covers a lot of good things such as:
- Authentication:
- Reading Data
- Writing Data
Dan’s posts talk about using the method of authentication where you create an app that contains an embedded browser control and you get the user using t your app to sign in to Salesforce. Then your app grabs the OAuth token and include that with all future requests. Before you do this you need to configure a new remote access app for your Salesforce account. This is done by logging in and going to SETUP > DEVELOP > REMOTE ACCESS. Click NEW and create a new remote access app. One of the fields it will give you is a callback URL that is required. This is used by Salesforce to redirect the user back to a specific URL after a successful sign in. In a web app this makes sense but not if you want to use this within a console app or a service. What I do is put just a dummy URN in there that I’ll look for later like resttest:callback.
Now, Dan’s stuff works great in many cases, but not if you want to create an app that runs like a service and automatically logs in and does some stuff with the API without user interaction. The way you authenticate with Salesforce is using OAuth 2.0. OAuth 2.0 supports multiple authentication flows and Salesforce supports many of them as outlined in their SDK: Authenticating Remote Access Application OAuth & discussed on their DeveloperForce site: Digging Deeper into OAuth 2.0 on Force.com. In my scenario I want to focus on creating an app that runs as a service to use the REST API without any user action required for logging in. This is the least secure because it means I have to store a username & password somewhere, but that’s just the ante for playing the game. So, in this case I want to use the OAuth 2.0 Username & Password Flow.
So, to authenticate you do something like this using the OAuth 2.0 Username & Password flow:
private void AuthenticateToSalesforce(){
string uri = "https://login.salesforce.com/services/oauth2/token";
var webClient = new WebClient() {
BaseAddress = uri
};
var collection = new NameValueCollection();
collection.Add("grant_type", "password");
collection.Add("client_id", CONSUMER_KEY);
collection.Add("client_secret", CONSUMER_SECRET);
collection.Add("username", SALESFORCE_USERNAME);
collection.Add("password", SALESFORCE\_PASSWORD\_AND_SECRETKEY);
byte\[\] responseBytes = webClient.UploadValues("", "POST", collection);
string response = Encoding.UTF8.GetString(responseBytes);
string decodedResponse = HttpUtility.UrlDecode(response);
JavaScriptSerializer js = new JavaScriptSerializer();
var token = js.Deserialize(decodedResponse);
}
public class TokenResponse {
public string id { get; set; }
public string issued_at { get; set; }
public string refresh_token { get; set; }
public string instance_url { get; set; }
public string signature { get; set; }
public string access_token { get; set; }
}
As you can see I’m creating a Web request and passing in a handful of things in the header. Specifically I’m passing in the grant_type of “password” to signal I want to use the OAuth 2.0 Username & Password flow. I’m also passing in the consumer key & secret you get from creating the remote access app in your Salesforce account and finally the username & password to authenticate with. This will return a JSON array (you can also get a XML response if you like) which I deserialize into a custom object that will pull everything I need. Specifically I need the instance_url (which is the URL of my Salesforce account) and the access_token which is what you need to include in all future requests.
Once you have the token, you can use the REST API to read & write data like Dan shows in his posts I linked to above, or like this:
public void QuerySalesforce(){
// build LEAD query
XDocument xDoc = XDocument.Load(@"SalesForceSchema.xml");
var fields = from x in xDoc.Root.Element("Lead").Elements("Field")
select x.Attribute("SfName").Value;
string query = String.Format("SELECT {0} FROM Lead
WHERE Email = '[email protected]'"
, string.Join(",", fields));
string sfQuery = String.Format("{0}/services/data/v23.0/query?q={1}",
token.instance_url,
query);
HttpWebRequest request = HttpWebRequest.Create(sfQuery) as HttpWebRequest;
request.Headers.Add("Authorization", "OAuth " \+ token.access_token);
request.ContentType = "application/json";
request.Method = "GET";
WebResponse webResponse = request.GetResponse();
var sr = new System.IO.StreamReader(webResponse.GetResponseStream());
string json = sr.ReadToEnd();
var leads = js.Deserialize>
(HttpUtility.UrlDecode(json));
}
public class sfdcLeadCollection {
public bool Done { get; set; }
public int TotalSize { get; set; }
public string nextRecordsUrl { get; set; }
public List Records { get; set; }
}
public class sfdcAttributes {
public string Type { get; set; }
public string Url { get; set; }
}
public class sfdcLeadForCollection : sObjectLead {
public sfdcAttributes Attributes { get; set; }
}
public class sObjectLead {
public string Id { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
public string Phone { get; set; }
public string MobilePhone { get; set; }
public string Fax { get; set; }
public string Website { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
public string PostalCode { get; set; }
}