Saturday, January 8, 2011

Salesforce REST API Record Creation

In my last few articles I showed the basics of how to connect to Salesforce using the REST API and how to use some basic functions. Now lets look at how to actually manipulate the data stored in SFDC. We will start by looking at how to insert a new record.
When you are working with tables (called objects in the SFDC nomenclature), the URI will look something like this:
http://na1.salesforce.com/services/data/v20.0/sobjects/Account/

  • http://na1.salesforce.com  is the instance URL that you get during the login process.
  • /services/data/v20.0/ indicates the version of SFDC you will be using.
  • sobjects/ is the resource we will be accessing, in this case the SFDC objects (tables).
  • Account/ is the name of the object we will be creating the new record in.
To insert a new record we will do an HTTP POST to this URI. In the headers we will pass the authorization token, and in the body we will pass the JSON encoded data we want to insert. For example here is a simple piece of JSON that will insert a new Account with the name TestAccount and number 1234567.
{    
"Name" : "TestAccount",
"AccountNumber" : "1234567"
}

Now lets look at the C# code to perform this insert. First, to simplify the creation of our JSON lets create an Account class that we can serialize.

public class sfdcAccount
{
public string Name { get; set; }
public string AccountNumber { get; set; }

}

Next we will setup our URI and the account object

var uri = instanceURL + "/services/data/v20.0/sobjects/Account/";

var acct = new sfdcAccount();
acct.Name = "NewAccount";
acct.AccountNumber = "123456";

and then serialize the account object into JSON.

var ser = new JavaScriptSerializer();
var body = ser.Serialize(acct);

We can now create our HTTP request.

var req = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(uri);
req.Headers.Add("Authorization: OAuth " + accessToken);
req.ContentType = "application/json";
req.Method = "POST";


Here we create an HttpWebRequest object pointing to the URI we specified earlier. We then set a header that contains the access token we got during the login process, set the content type to JSON and finally specify that this is an HTTP POST request.

Next we need to add the body of the request. This code will convert our JSON to a byte array an attach it to the request.

byte[] data = System.Text.Encoding.ASCII.GetBytes(body);
req.ContentLength = body.Length;
var os = req.GetRequestStream();
os.Write(data, 0, data.Length);
os.Close();

Finally we execute the request and retrieve the result that SFDC sends back.

WebResponse resp;

try
{
resp = req.GetResponse();
}
catch (WebException ex)
{
resp = ex.Response;
}

if (resp == null) return null;
System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream());
return sr.ReadToEnd().Trim();

The try/catch around the request is important. When there is an error executing the command SFDC will return a HTTP response code of 400 which will throw an exception. To resolve this problem we catch the exception and retrieve the response object from the exception.

Assuming there are no errors, SFDC will return a JSON response that looks like this

{
"id":"0017000000hX4BIAA0",
"errors":[],
"success":true
}

First we get back the internal id of the record that was created. We can use this later to read, update, or delete this record. We also get back a true/false field indicating the success or failure of the insert and an errors field. I have not yet found a case where these are used since an error will return a different response. For example if we make the AccountNumber greater the 40 characters we will get the following response:

{
"fields":["AccountNumber"],
"message":"Account Number: data value too large: 1234567890123456789012345678901234567890123456789012345678901 (max length=40)",
"errorCode":"STRING_TOO_LONG"
}

7 comments:

Timo said...

Great article. Thanks that you sharing us this.

Shailesh said...
This comment has been removed by the author.
Shailesh said...

Thanks for article!!
I've a question here;

I've created my own class with methods which creates Leads and contacts in Salesforce account.

How can we pass json string as a request instead of sending parameters in query string to @httpPost using C#?
Can we use @HttpPost to perform insert and update operation in one go?

Dan Boris said...

@Shailesh: I am not sure what your question is? The JSON is not passed as a query string, it IS passed as part of the request.

Unknown said...

Tks! Very helpfull article!

Can u update it or create a new version using HttpClient instead HttpWebRequest ?!

Im trying to use HttpClient, but its not working to POST and PUT, just with GET methods.

I always receive a message: "Can not desrialize SObject out of VALUE_STRING token. errorCode: JSON_PARSE_ERROR";

Im using the same json at WebRequest and HttpClient. With WebRequest, following your example, it works well..

Here is my code with HttpClient:

var uri = "https://na15.salesforce.com/services/data/v27.0/sobjects/Account";

var acc = new Account();
acc.Name = "RestAPIHttpClient";

var ser = new JavaScriptSerializer();
var body = ser.Serialize(acc);

HttpClient client = new HttpClient();

client.DefaultRequestHeaders.Add("Authorization", "Bearer " + binding.SessionHeaderValue.sessionId);

var response = await client.PostAsJsonAsync(uri, body);
var stringresponse = await response.Content.ReadAsStringAsync();
Console.WriteLine(stringresponse);

Many tks in advance!

Anonymous said...

I keep getting a 'The remote server returned an error: (400) Bad Request' trying to update a record in Salesforce.

Below is my code. Any help would be much appreciated

Public Sub UpdateOrders()
Dim Address As Uri = New Uri("https://centerpoint.force.com/REPs/services/data/v30.0/sobjects/Order__c/")
Dim request As HttpWebRequest
Dim response As HttpWebResponse = Nothing
Dim reader As StreamReader
Dim postStream As Stream = Nothing
Dim serializer As JavaScriptSerializer = New JavaScriptSerializer()
Dim byteArray() As Byte

'Create the data to send
Dim ID As String
Dim dt As DataTable = cOrders.API_TrueCost_GetOrdersToUpdate
If dt.Rows.Count > 0 Then
For i As Integer = 0 To (dt.Rows.Count - 1)
Dim dr As DataRow = dt.Rows(i)
Dim Orders As New List(Of Order)()
Orders.Add(New Order With {.Order_Status__c = dr.Item("Order_Status__c").ToString()})


ID = dr.Item("Id").ToString

'Dim Method = New HttpMethod("PATCH")

'Create the web request
request = DirectCast(WebRequest.Create(Address.ToString + ID), HttpWebRequest)

'Add token to header
request.Headers.Add("Authorization", "Bearer " + _AccessToken)

'Set type to POST
request.Method = "PATCH"
request.ContentType = "application/json"

'create byte array for data to send
Dim serializedResult = serializer.Serialize(Orders.First)
byteArray = UTF8Encoding.UTF8.GetBytes(serializedResult.ToString())

'Set the content length in the request headers
request.ContentLength = byteArray.Length


'Write data
postStream = request.GetRequestStream()
postStream.Write(byteArray, 0, byteArray.Length)

Try
'Get Response
response = DirectCast(request.GetResponse(), HttpWebResponse)

'Get the response stream into a reader
reader = New StreamReader(response.GetResponseStream())

cOrders.API_TrueCost_UpdateOrders(dr.Item("Id").ToString())
Catch ex As Exception
Throw ex
Finally
If Not postStream Is Nothing Then postStream.Close()
End Try
Next i
End If

End Sub

Dan Boris said...

@nas, check out this post which explains how to further troubleshoot this sort of error...

http://danlb.blogspot.com/2013/09/salesforce-rest-exceptions.html