1
votes

How do I configure an ASP.NET web form to send requests via wsHttpBinding rather than basicHttpBinding when querying CRM Online?

As a bit of background this is a pretty basic ASPX page hosted locally in IIS 8.5.
The page is meant to connect to CRM Online and pull some data.
Edit: This app is authenticated via Azure AD which is why it uses a client ID and key. I want to shy away from using connection strings and domain credentials unless this is unavoidable.

I think I've got the authentication to CRM correct but when I run a query I receive the error below:

System.Net.WebException: The remote server returned an error: (415) Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'..

I think this is because I haven't specified a binding in web.config so my page is sending everything via basicHttpBinding and the CRM Online interface is expecting wsHttpBinding.

I've tried using the example at the bottom of this MSDN article (and variations thereof) but it makes no difference.

Here's the code for connecting to CRM in case it makes a difference. Obviously I've obscured the real configuration details.

// CONFIGURE OAUTH
var tenantName = "mycompany.onmicrosoft.com";
var authString = string.Format(@"https://login.microsoftonline.com/{0}",tenantName);
var authContext = new AuthenticationContext(authString, false);
var clientId = "123ab123-123a-1a23-abcd-1a2345612345";
var key = "nStgfrdk0oyaC1P5+/FQ4wGn4fRgUTr8HTKejytf0bv=";
var clientCred = new ClientCredential(clientId, key);
var resource = "https://myinstance.crm4.dynamics.com";

// ACQUIRE THE AUTH TOKEN
var authResult = await authContext.AcquireTokenAsync(resource, clientCred);

// CREATE THE CONNECTION TO CRM
var orgService = new OrganizationWebProxyClient(
    new Uri("https://dev.crm4.dynamics.com/XRMServices/2011/Discovery.svc"), true)
    {
        HeaderToken = authResult.AccessToken,
        SdkClientVersion = "8.1.0"
    };

// RUNNING THIS QUERY CAUSES THE ERROR
var contacts = orgService.RetrieveMultiple(new QueryExpression
{
    EntityName = "contact",
    ColumnSet = new ColumnSet("firstname", "lastname")
})
.Entities
.Select(item => item.ToEntity<Contact>());

Here's the stack trace in case it's of use:

[WebException: The remote server returned an error: (415) Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'..] System.Net.HttpWebRequest.GetResponse() +1740 System.ServiceModel.Channels.HttpChannelRequest.WaitForReply(TimeSpan timeout) +75

[ProtocolException: Content Type text/xml; charset=utf-8 was not supported by service https://dev.crm4.dynamics.com/XRMServices/2011/Discovery.svc. The client and service bindings may be mismatched.] System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) +14350190 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) +388 Microsoft.Xrm.Sdk.IOrganizationService.RetrieveMultiple(QueryBase query) +0 Microsoft.Xrm.Sdk.WebServiceClient.WebProxyClient1.ExecuteAction(Func1 action) +51 Quote_Robot_Demo.d__4.MoveNext() +887 System.Runtime.CompilerServices.<>c.b__6_0(Object state) +56 System.Web.Util.SynchronizationHelper.SafeWrapCallback(Action action) +110 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61 System.Web.Util.WithinCancellableCallbackTaskAwaiter.GetResult() +32 System.Web.UI.d__523.MoveNext() +7970

Edit: Once I added the service reference I got this in my web.config:

  <system.serviceModel>
<bindings>
  <customBinding>
    <binding name="CustomBinding_IDiscoveryService">
      <textMessageEncoding />
      <httpsTransport />
    </binding>
  </customBinding>
</bindings>
<client>
  <endpoint address="https://dev.crm4.dynamics.com/XRMServices/2011/Discovery.svc"
    binding="customBinding" bindingConfiguration="CustomBinding_IDiscoveryService"
    contract="CRMService.IDiscoveryService" name="CustomBinding_IDiscoveryService" />
</client>
</system.serviceModel>
1
I should have specified that this app is authenticated via Azure AD so it uses a unique client ID and key provided by Azure to sign in. The SDK examples use connection strings but they require Windows credentials and I don't want to do that. I'll edit my question to be more specific about this. - Equalsk
CRM SDK is not just for on-premise installations, it also supports CRM online version, so no matter how your app is hosted, you can use the SDK to connect to a CRM instance, no matter how it is hosted. Also, SDK in the background does exactly what you are trying to achieve, it handles all the communication channels for you. - dynamicallyCRM
I know the SDK is also for CRM Online, I'm using SDK classes in the code above... I don't think I understand what you mean by saying the SDK does everything for me, it clearly doesn't default to the correct SOAP version (1.2) which is why I get the error. - Equalsk
Please post the <system.serviceModel> section of your web.config. - Clint B

1 Answers

0
votes

Although I now have some other issues I did discover how to alter the binding.
You can add the organization service as a service reference to your project as I did and then assign the binding like this:

orgService.Endpoint.Binding = new CustomBinding("CustomBinding_IOrganizationService");

The string passed to the custom binding is just the name taken from the config:

<system.serviceModel>
  <bindings>
    <customBinding>
      <binding name="CustomBinding_IOrganizationService">
        ....

Edit: I discovered later on that changing the binding was not necessary. The error seemed to be being caused by an invalid security token. I changed the way I generated security tokens to use the grant_type of password and the error went away on its own.