Tuesday, May 13, 2014

Simple CRM Lead Capture using Azure & Web API

I wanted to show a fairly simple example solution for capturing data from a public web page and sending it to Dynamics CRM. If you do a bit of looking you can probably find a whole bunch of examples on creating strictly .NET based solutions that communicate directly with CRM. This is great of course if your site already is written in .NET. Those whose sites are just static pages or have been created using other languages might find incorporating in a .NET page too much of a hassle.

You could create a strictly JavaScript based solution that talks directly to CRM but that ultimately leaves the credentials used to authenticate exposed. This example walks through creating and deploying a simple .NET Web API proxy application running on Microsoft Azure that will handle communication with CRM. You can run up to 10 web sites for free (so long as you stay under 5GB of outbound bandwidth per month) on Azure so you won’t have to pay any additional hosting costs to set something like this up.

Getting the Project Set Up


Using Visual Studio 2013 create a New Web Project

1

Choose Web API

For the purposes of this post we won’t worry about implementing authentication as we will be implementing some measures to control access based on the origin of the request later on. Under the Azure heading, uncheck the box to create remote resources, we’ll handle that later.

2

Add the following reference DLLs to the project:
  • Microsoft.Xrm.Client (located in the SDK bin folder)
  • Microsoft.Xrm.Sdk (located in the SDK bin folder)
  • Microsoft.IdentityModel (located in C:\Program Files\Reference Assemblies\Microsoft\Windows Identity Foundation\v3.5 – you may need to install WIF - Windows Identity Foundation)
Right click on each of these references and make sure Copy Local is set to True – this is likely not the case for Microsoft.IdentityModel. When this gets published to Azure we need to ensure this DLL gets copied up to the local bin folder along with the rest of the files. When you install WIF on your local machine this gets installed into the GAC but on the shared Azure server it won’t be there.

3

Create another Web Project under the solution, we will use this for testing the form which will submit data to CRM via the Web API application.
In that project, add a basic HTML page named index.html.
We’ll want to use jQuery in this project to submit the form, an easy way to add it is to use NuGet. From the Tools menu in Visual Studio, select NuGet Package Manager and then Manage NuGet Packages for Solution.

4

Find and install jQuery into just the website test project. The result will be latest jQuery files being installed to the Scripts folder.

5

Once you’ve got that done, go ahead and add the jQuery script reference to the header of index.html. An easy way is just to drag the file from Solution Explorer into the <head> tag and Visual Studio will create it for you.

Open NuGet once again and this time search for Microsoft.AspNet.WebApi.Cors. Install it to the Web API project so later on we can can tell the calling browser it's OK to accept the response we send back. Modern browsers don’t like to allow access to the responses from resources that aren't in the same domain because of the potential security risk, this is referred to as cross-origin resource sharing or CORS. We are going to deploy the Web API project to a free Azure site which ends up on the azurewebsites.net domain which is likely not going to be the same as the yourdomain.com website you're going to be capturing data from.

7

Show Me the Codez

We’ll need to connect to CRM to create the Leads so we'll store the connection information in the web.config file in our Web API project. An easy way to find out the connection string format for your specific deployment is to grab the sample connection strings from the app.config file in the \SDK\SampleCode\CS\QuickStart sample application. This implementation will be a little simpler than the example in SDK, I’d also suggest renaming the connection to something a little more manageable.

8_thumb3

In the Web API project, open WebApiConfig.cs in the App_Start folder.
To the top of the page add:
  • using System.Net.Http.Headers;
In the Register function add:
config.EnableCors();
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(
    new MediaTypeHeaderValue("text/html"));

The first line gives us the option a bit later to limit which domains and HTTP methods we allow access to. The second defaults the Web API application to return data in JSON format by default.

In the Web API project, right click on the Controllers folder and add a new Controller. Name it CRMLeadController.

9_thumb1

When prompted, choose the Web API 2 Controller with read/write actions.

10_thumb1

To the top of CRMLeadController.cs add:
  • using System.Configuration;
  • using Microsoft.Xrm.Client;
  • using Microsoft.Xrm.Client.Services;
  • using Microsoft.Xrm.Sdk;
  • using System.Web.Http.Cors;
  • using System.Net.Http.Formatting;

Take note of all the controller methods that were created by default that correspond to their HTTP counterparts, particularly if you need to create other forms of integration later on. For this example you can replace the contents of the class with the following code:
private OrganizationService _orgService;

[EnableCors(origins: "http://www.yourdomain.com", headers: "*", methods: "post")]
public string Post([FromBody] FormDataCollection formValues)
{
    string domain = HttpContext.Current.Request.Headers["Origin"].ToLower();
    string host = HttpContext.Current.Request.Url.Host.ToLower();
    if (!domain.Contains("yourdomain.com") && !domain.Contains(host))
        return "fail!";

    CrmConnection connection = CrmConnection.Parse(
        ConfigurationManager.ConnectionStrings["CRMOnlineO365"].ConnectionString);

    using (_orgService = new OrganizationService(connection))
    {
        Entity lead = new Entity("lead");
        lead["firstname"] = formValues.Get("FirstName");
        lead["lastname"] = formValues.Get("LastName");

        _orgService.Create(lead);
    }
    return "success";
}

So what’s going on here? First thing is we are specifying which domain(s) we're going to allow access to in the 'origins' parameter of the EnableCors attribute. Adding specific domains here doesn't actually prevent the request from getting fulfilled, it only effects if the browsers can access the response or not.

For additional background information on the topic: Enabling Cross-Origin Requests in ASP.NET Web API.

To help ensure someone doesn't get ahold of the URL for this service to pump a bunch of bad records into your CRM, we added a simple check to verify the origin domain is one you specified or in the same domain as the Web API application. Keep in mind this isn't a fool proof method but it's simple enough for our purposes here. Next we are grabbing our CRM connection string from the web.config file and creating a connection to the CRM organization. Once we’ve established the connection we're free to write data (so long as the permissions the accompany the credentials you've supplied in the connection string allow it). Then its just a matter of reading the values sent across when the form submitted and setting the appropriate attributes on the Lead record we are going to create. Keep in mind in this example we aren't doing any validation or transforming the the data to anything besides a string. Once that's done we create the record and return a value back to indicate the operation completed.

Ideally we'll want to test this out before trying to incorporate anything into an existing production website. To get started add a basic form with a couple fields to the body of the index.html page you created earlier. It's important to note here to make sure you include the Name attribute on the input elements. When the FormDataCollection is read in the earlier code, the input elements Name becomes the key value used to find each specific value.
<form id="LeadForm">
    <label for="FirstName">First Name</label>
    <input type="text" id="FirstName" name="FirstName" />
    <br />
    <label for="LastName">Last Name</label>
    <input type="text" id="LastName" name="LastName" />
    <br />
    <input type="submit" value="Submit" />
</form>

Next up we'll need to add some JavaScript to the page which will handle submitting the data to the Web API application and handling the response.
$(document).ready(function () {
    $("#LeadForm").submit(function (event) {
        $.ajax({
            type: "POST",
            url: "http://localhost:1611/api/CRMLead",
            data: $("#LeadForm").serialize(),
            dataType: 'json',
            success: function (response) {
                alert(response);
                $('#LeadForm').each(function () {
                    this.reset(); //Reset each field
                });
            },
            error: function (request, textStatus, errorThrown) {
                alert(request.responseText + " " + 
                    textStatus + " " + errorThrown);
            }
        });
        event.preventDefault();
    });
});
    
Here we are intercepting the native submit functionality that the form provides and replacing it with a jQuery.ajax request which takes the serialized form data and sends it to the Web API application. Keep in mind for testing you will need to adjust the port number on the URL to match whatever is generated when you debug on your local machine. Later on when the Web API application is deployed, you'll need to again to change the URL to reflect its new home. If everything goes according to plan the data will be submitted and a Lead will be created in CRM, when the request is completed on the server end the message we sent back should be displayed and the form fields reset.

In order to test everything out, first open your test form page in a browser. A quick way here is to right click on the page and select View in Browser. Ultimately we want to have this page open in a browser when we start debugging the Web API application. So either grab the URL to re-open the page in another browser.

Now you can start debugging the Web API application (hopefully you've resolved any issues and it fires up). Once it is running you can open up the test form in a new tab or different browser. Go ahead and complete the form and if everything works you should see the Lead appear in CRM.

Here it is in action:

12

Publishing to Microsoft Azure


Once the publish process is complete you'll probably want to test again using the test page we created. So go back to CRMLeadController.cs and on the Post method, update the 'origins' parameter on the [EnableCors] attribute to "*" (all domains) or "http://localhost". Once you are ready to integrate with an existing site, you can change this to the proper domain name and re-publish the application.

When you are ready, right click on the Web API project and select Publish.

Select Windows Azure Web Sites for the target. You'll have to sign in to your account if you aren't already connected at this point. Then choose to create a new web site. From there you can pick name and region you'd like the site to be hosted in.

13_thumb1_thumb

At this point there really isn't any left, we can accept the default values here and publish away.

14_thumb1_thumb

If the publish is successful you should see a browser open pointed at the freshly deployed Web API site.

At this point we can go ahead on update our test web page to submit to our new Azure site to make sure everything is working.

15_thumb1_thumb

Hopefully once you submit the form you should again see the success message. If not you can always debug your JavaScript or use the awesome remote debugging functionality for Azure that is incorporated into Visual Studio 2013.

Good luck.

You can down load the source for this project here:

https://github.com/jlattimer/CRMLeadCaptureExample