1 Comments

(Puedes ver este artículo en español aquí)

In a previous article, A .Net WCF Service Handler for Salesforce Workflow Outbound Messages that Calls Back to Salesforce using SOAP API, I explained how to create a .Net WCF service to handle an outbound message and how to get additional data from Salesforce using the SOAP API. In this article I use the same example but instead of using the SOAP API I will use the REST API. You might wonder why do I need to change my SOAP code to use REST instead, and the answer is simple: you might have the SOAP API disabled (because of the Salesforce edition you have) in your organization and only have the REST API available.

Allow me to refresh what we would like to accomplish: our client wants to integrate accounts in Salesforce with accounts in their ERP, so every time a new opportunity is marked as “Closed Won” in Salesforce the account is created on the ERP. I suggest you to read the previous article which explains how to set things up for the workflow and the Visual Studio project to create the WCF service that handles the workflow outbound message.

Salesforce is able to expose its metadata as a REST service. As we did in the case of SOAP, we could use the REST API to query the account information, but this could be simplified by exposing an APEX class as a REST service. Salesforce makes this very simple. You can follow the steps explained in the Force.com Apex Code Developer's Guide for an overview on how to do this. In this article I will expose an APEX class as a REST service and then I will consume this service from our message handler created in .Net

Exposing the REST Service in Salesforce

This is actually very simple, all you need to do is open the Salesforce Developer Console and from the menu select “File->New->Apex Class”. Name it AccountRestService and replace the code with the following:

@RestResource(urlMapping='/Account/*')
global with sharing class AccountRestService {
    @HttpGet
    global static Account doGet() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;
        String accountId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
        Account result = [SELECT Id, Name, BillingStreet, BillingCity, BillingState, BillingPostalCode FROM Account WHERE Id = :accountId];
        return result;
    }
}

Notice in line 1 that we use the special @RestResource to tell Salesforce that this is actually a REST service. In line 3 we specify that the doGet method will be called by HTTP GET. The URL for this service will be the following:

https://{instanceName}.salesforce.com/services/apexrest/Account/{accountId}

The https://{instanceName}.salesforce.com/services/apexrest is the URL base address for all REST services, and the /Account/{accountId} is specified by our class definition in the urlMapping parameter of the @RestResource tag. For example you could use the following URL to get the details for a specific account:

https://eu5.salesforce.com/services/apexrest/Account/00124000002uzps

There is one thing we haven’t considered yet: security. If you put the above URL into a browser you will get an INVALID_SESSION_ID error. If you read the documentation you will learn that the HTTP request issued against the REST service needs an Authorization HTTP header. You could create a connected app and use OAuth to call the login REST service and get a session id, but this is actually complex (I will explain it in a future article) but in our case, since we are calling this service from an outbound message handler, we already have the session id. Remember from the previous article that we marked the outbound message to “Send Session ID”:

Outbound message

So, all we need to do is to build the right HTTP request from our WCF message handler to call our REST service.


Calling the REST Service from Visual Studio

Open the Visual Studio project you created in the previous article (you can get a sample from here). We will use RestSharp as our REST client to call the service. Using NuGet, add the RestSharp package to your project. RestSharp can automatically transform the JSON text returned from a REST service to a strong typed object. Let’s create a model to encapsulate the data returned from the REST service: in Visual Studio, create a Model folder and add a class named Account to it. Replace the code with the following:

namespace WorkflowNotificationServices.Model
{
    public class Account
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string AccountNumber { get; set; }
        public string BillingStreet { get; set; }
        public string BillingCity { get; set; }
        public string BillingState { get; set; }
        public string BillingPostalCode { get; set; }
        public string BillingCountry { get; set; }
    }
}

We have defined an account class that encapsulates the data from the account object in Salesforce. Notice that for simplicity we have named the properties the same as the object fields in Salesforce (you don’t have to name things the same, you could use JSON.Net to get around it, but is not a topic we would like to do in this article).

Now, open the file OpportunityNotificationService.svc.cs and change the method CreateAccount with the following code:

private bool CreateAccount(string url, string sessionId, WorkflowNotificationServices.Opportunity opportunity)
{
    int recordsAffected = 0;

    Uri uri = new Uri(url);
    RestClient restClient = new RestClient(new Uri(String.Format("https://{0}", uri.Host)));
    RestRequest request = new RestRequest("services/apexrest/Account/{id}");
    request.AddUrlSegment("id", opportunity.AccountId);
    request.AddHeader("Authorization", String.Format("Bearer {0}", sessionId));
    IRestResponse<Model.Account> response = restClient.Execute<Model.Account>(request);

    Model.Account account = response.Data;

    ConnectionStringSettings connectionString = ConfigurationManager.ConnectionStrings["ERP"];
    using (SqlConnection cn = new SqlConnection(connectionString.ConnectionString))
    {
        using (SqlCommand command = new SqlCommand("salesforce_crearCliente", cn))
        {
            command.CommandType = CommandType.StoredProcedure;

            command.Parameters.Add("@idSalesforce", SqlDbType.VarChar).Value = account.Id;
            command.Parameters.Add("@name", SqlDbType.VarChar).Value = account.Name;
            command.Parameters.Add("@number", SqlDbType.VarChar).Value = (object)account.AccountNumber ?? DBNull.Value;
            command.Parameters.Add("@address", SqlDbType.VarChar).Value = (object)account.BillingStreet ?? DBNull.Value;
            command.Parameters.Add("@city", SqlDbType.VarChar).Value = (object)account.BillingCity ?? DBNull.Value;
            command.Parameters.Add("@state", SqlDbType.VarChar).Value = (object)account.BillingState ?? DBNull.Value;
            command.Parameters.Add("@postalCode", SqlDbType.VarChar).Value = (object)account.BillingPostalCode ?? DBNull.Value;
            command.Parameters.Add("@country", SqlDbType.VarChar).Value = (object)account.BillingCountry ?? DBNull.Value;

            cn.Open();
            recordsAffected = command.ExecuteNonQuery();
        }
    }

    return recordsAffected > 0;
}

The lines highlighted are the important ones. Notice in line 6 how we use the URL we got from the SOAP message sent by Salesforce to our WCF service and we get the host (and thus the instance name) we need to send the HTTP request to. In line 7 we create an HTTP request using RestSharp and specify the endpoint of our REST service, as explained before. In line 8 we specify the account Id we got from the SOAP of the outbound message on opportunity. In line 9 we set the security part we need to make this work. We need to set the Authorization header to the value Bearer {sessionId}. The session id we get it again from the SOAP of the outbound message sent by Salesforce (remember we marked the “Send session ID” field in the outbound message definition). Finally, in line 12 we make the HTTP call and tell RestSharp to convert the result to our Account object we created before. The rest of the code is just the same, using the values returned by the REST service (now strongly typed into a class) to call a stored procedure on the ERP.


Testing the Call Back

To test our web service we just follow the same steps outlined in the previous article. Notice that we only changed the API used to obtain data from Salesforce: we were using SOAP before and now we use REST

You can get the sample project here:

Comments

Comment by Sumit Datta

Nice Post helped me a lot, Thank you for uploading