Accessing raw SOAP requests/responses from Dynamics CRM web services in C#
One of the things I have always found frustrating about WCF is that it effectively hides the actual SOAP message XML requests and responses in web service calls. From a Dynamics CRM perspective, I can think of at least three good reasons it would be nice to be able to access the raw XML generated and consumed by clients built with the SDK:
- Display retrieved results with XSL like I demonstrated in this client-side FetchXML example
- Log the raw messages for QA, auditing or regulatory reasons
- Process the XML to easily generate "wrapper" interfaces
In this post I will show how to capture client requests and service responses for interfaces built with the CRM SDK as well as interfaces that use a WSDL-based proxy using a client message inspector.
The message inspector class
The easiest way to capture the actual SOAP messages is to use a client message inspector object (IClientMessageInspector). The BeforeSendRequest and AfterReceiveReply methods of the client message inspector will give you the SOAP request and response as string objects that you can process however you want. Here is a class called MyInspector that stores requests and responses in list objects called sentMessages and receivedMessages respectively MyInspector.cs (1.55 kb). The original source for this code is http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/fa3f4c55-5835-43f2-892f-737b4bccb598.
Using the message inspector
Once you have your message inspector ready, you have to have to update the behaviors for the destination endpoint to use it. If you are using the SDK, all you have to do is instantiate a new MyInspector object and then add it to your OrganizationServiceProxy's ServiceConfiguration.CurrentServiceEndpoint behaviors. The code below contains a helper function that creates a new OrganizationServiceProxy object and adds the message inspector.
static OrganizationServiceProxy createProxy(string orgUri, string username, string password)
{
ClientCredentials credentials = new ClientCredentials();
Uri organizationUri = new Uri(orgUri);
IServiceConfiguration<IOrganizationService> config =
ServiceConfigurationFactory.CreateConfiguration<IOrganizationService>(
organizationUri);
credentials.UserName.UserName = username;
credentials.UserName.Password = password;
OrganizationServiceProxy serviceProxy = new OrganizationServiceProxy(config, credentials);
MyInspector inspector = new MyInspector();
serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(inspector);
return serviceProxy;
}
If you are using WSDL-based proxy, adding the message inspector is slightly different, but just as simple. As with the SDK, you first have to instantiate a new MyInspector object, but you then add it to your WCF client's Endpoint.Behaviors object. The code below shows an version of the ExecuteWhoAmI method from the CRM SDK WsdlBasedProxies sample with the message inspector added.
private static void ExecuteWhoAmI(ClientCredentials credentials, string serviceUrl)
{
using (OrganizationServiceClient client = new OrganizationServiceClient("CustomBinding_IOrganizationService",
new EndpointAddress(serviceUrl)))
{
ApplyCredentials(client, credentials);
MyInspector inspector = new MyInspector();
client.Endpoint.Behaviors.Add(inspector);
OrganizationRequest request = new OrganizationRequest();
request.RequestName = "WhoAmI";
OrganizationResponse response = (OrganizationResponse)client.Execute(request);
foreach (KeyValuePair<string, object> result in response.Results)
{
if ("UserId" == result.Key)
{
Console.WriteLine("User ID: {0}", result.Value);
break;
}
}
}
}
Accessing the messages
Once you have added the message inspector to your endpoint behaviors, you are ready to call the CRM web service and access the request and response messages. Here is an updated version of the ExecuteWhoAmI method that writes the request and response messages to the console:
private static void ExecuteWhoAmI(ClientCredentials credentials, string serviceUrl)
{
using (OrganizationServiceClient client = new OrganizationServiceClient("CustomBinding_IOrganizationService",
new EndpointAddress(serviceUrl)))
{
ApplyCredentials(client, credentials);
MyInspector inspector = new MyInspector();
client.Endpoint.Behaviors.Add(inspector);
OrganizationRequest request = new OrganizationRequest();
request.RequestName = "WhoAmI";
OrganizationResponse response = (OrganizationResponse)client.Execute(request);
foreach (KeyValuePair<string, object> result in response.Results)
{
if ("UserId" == result.Key)
{
Console.WriteLine("User ID: {0}", result.Value);
break;
}
}
Console.WriteLine("****REQUEST****");
foreach (string sent in inspector.sentMessages)
{
Console.WriteLine(sent);
Console.WriteLine();
}
Console.WriteLine("****RESPONSE****");
foreach (string received in inspector.receivedMessages)
{
Console.WriteLine(received);
Console.WriteLine();
}
Console.ReadLine();
}
}