Consume an AX7 custom web service by SOAP endpoint

Dynamics 365 for Operations (a.k.a. AX7) provides several endpoints for web service. In this blog post, I want to describe consuming a D365O custom web service in a C# application using the SOAP endpoint.

For a detailed description about service endpoints, you can read the official documentation at https://docs.microsoft.com/en-us/dynamics365/operations/dev-itpro/data-entities/services-home-page.

The main advantage of the SOAP protocol is its descriptive functionality through the WSDL language. SOAP endpoints provide detailed description about contracts and parameters to call each service method. Visual Studio has a great functionality that can read the service description and automatically generate proxy classes to access the service methods.

Let’s do an example of consuming a D365O web service in Visual Studio. Continue reading

Creating oData actions and testing with Fiddler

I have been experimenting with oData actions and testing them using Fiddler. Overall it is easy to implement them and they can be used as a “cheap” alternative to custom services. Compared to a custom service, the Data Entity can act as the data contract and removes the need of querying/saving a complicated table structure.

There are two kind of actions you can add, collection level (static) actions and instance level actions. We are going to use the Fleet Management module in order to create one action of each type and then test it using Fiddler. Continue reading

Testing AX7 OData services with Fiddler

The integration methods available in Dynamics 365 for Operations (nicknamed AX7) have changed considerably, compared with AX 2012 or earlier versions. The SOAP document services are gone, so are the file-based integrations that read or write files in a local or network file folder. These have been replaced by a collection of RESTful APIs which allow you to do file upload/download or run real-time data queries or updates.

This wiki article by Microsoft explains the basics of real-time scenarios pretty well:

https://ax.help.dynamics.com/en/wiki/dynamics-ax-7-services-technical-concepts-guide/

The wiki article also refers to code examples that demonstrate connecting to AX from an external .NET (C#) application. The problem is, how can someone without any .NET skills understand how to test these services or build non-.NET clients? One can run GET requests to query data using a web browser, but what about data updates? Hopefully this will be documented by Microsoft in more detail but, in the meantime, I am sharing my experience. The below examples are for consuming AX OData services, but the same testing principles can also be applied to custom services or even file enqueueing/dequeueing. Obviously, the same approach can be used with any RESTful client tool, not just Fiddler.

Before starting, a few words on how I worked out the steps below . I built my own .NET demo application based on the examples from Microsoft. I ran it and traced the HTTP traffic with Fiddler to see how the C# code was transformed to HTTP requests by the .NET libraries. Then I cleaned up the HTTP requests a bit and ran them manually through Fiddler. And it worked!

I have tried the steps below only on an Azure-hosted AX instance. If anyone has experience with connecting to local AX VMs, I’d love to hear from you.

1. Set up your AX for service-to-service authentication
It took some digging to find out how to do this. The best instructions I found are in the first half of this article: https://community.dynamics.com/ax/b/axforretail/archive/2016/09/26/service-to-service-authentication-in-ax7

As a result, you will have two pieces of information: Client ID and Client Secret. Keep them secure, as they will allow anyone to connect to your AX instance.

You will also need to know the name of the AAD tenant and the base URL or your AX instance.

Note: service-to-service authentication is available from Platform Update 2 onwards. For earlier versions, you need to use username/password authentication to obtain the authorisation token (not covered by this article).

2. Start up Fiddler

image

Turn on HTTP capturing by clicking on the “Capturing” on the status bar. You will be defining your own requests, not monitoring others.

2. On the upper right, select Composer
This is where you define your HTTP requests to run.

3. Go to Options tab and switch on “Inspect Session” and “Fix Content-Length header”
This is for convenience – it automatically calculates the Content-Length for the request header, so you do not have to specify it. And after executing the request, it will automatically switch to Inspector mode to show the result.

image

4. Go to Scratchpad tab under Composer
This is a nice tool to store various test requests for re-use. Even if you close and reopen Fiddler, the test scripts are remembered. You can put all your test requests there and select which one to run.

5. Prepare the OAuth request
Replace the values in the template with correct tenant, AX base URL, client ID, client secret. Note the empty line between the headers and the body – it has to be there.

Note that in the body, some characters must be replaced by escape sequences (not an exhaustive list):

%3A :
%2F /
%3D = (may appear at the end of the client secret)

Request template:

POST https://login.windows.net/<tenant>/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: login.windows.net

resource=https%3A%2F%2F<axbaseurl>&client_id=<clientid>&client_secret=<clientsecret>&grant_type=client_credentials

6. Prepare the OData requests
Again, replace the value for your AX base URL. You will get the access token later. Obviously you also need to specify the correct data entity name and data. Note the empty line between the headers and the body – it has to be there.

Beware: the entity names and field names (even in the URL) are case sensitive.

OData GET example (to query data):

GET https://<axbaseurl>/data/CustomerGroups HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
Authorization: Bearer <authtoken>
Host: <axbaseurl>

OData POST example (to insert a record):

POST https://<axbaseurl>/data/CustomerGroups HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json;odata.metadata=minimal
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
Authorization: Bearer <authtoken>
Host: <axbaseurl>

{
"@odata.type":"#Microsoft.Dynamics.DataEntities.CustomerGroup",
"CustomerGroupId":"T6",
"dataAreaId":"USRT",
"Description":"Test group T6"
}

OData PATCH example (to update a record):

PATCH https://<axbaseurl>/data/CustomerGroups(CustomerGroupId='T6',dataAreaId='usrt') HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json;odata.metadata=minimal
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
Authorization: Bearer <authtoken>
Host: <axbaseurl>

{
"@odata.type":"#Microsoft.Dynamics.DataEntities.CustomerGroup",
"CustomerGroupId":"T6",
"dataAreaId":"usrt",
"Description":"Test group T6 newname"
}

OData batch example (multipart request to insert a journal header and 2 lines as a single transaction):

POST https://<axbaseurl>/data/$batch HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: multipart/mixed; boundary=batch_boundary
Accept: multipart/mixed
Accept-Charset: UTF-8
Authorization: Bearer <authtoken>
Host: <axbaseurl>

--batch_boundary
Content-Type: multipart/mixed; boundary=changeset_boundary

--changeset_boundary
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1

POST https://<axbaseurl>/data/LedgerJournalHeaders HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json;odata.metadata=minimal
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
Authorization: Bearer <authtoken>

{
"@odata.type":"#Microsoft.Dynamics.DataEntities.LedgerJournalHeader",
"Description":"Test Journal 90001",
"JournalBatchNumber":"90001",
"JournalName":"GenJrn"
}
--changeset_boundary
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2

POST https://<axbaseurl>/data/LedgerJournalLines HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json;odata.metadata=minimal
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
Authorization: Bearer <authtoken>

{
"@odata.type":"#Microsoft.Dynamics.DataEntities.LedgerJournalLine",
"AccountDisplayValue":"601200-006--033",
"CurrencyCode":"USD",
"DebitAmount":10.05,
"JournalBatchNumber":"90001",
"LineNumber":2,
"OffsetAccountDisplayValue":"",
"Text":"Test Line 1",
"TransDate":"2016-12-09",
"Voucher":"GNJL000101"
}
--changeset_boundary
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3

POST https://<axbaseurl>/data/LedgerJournalLines HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json;odata.metadata=minimal
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
Authorization: Bearer <authtoken>

{
"@odata.type":"#Microsoft.Dynamics.DataEntities.LedgerJournalLine",
"AccountDisplayValue":"601200-006--033",
"CreditAmount":10.05,
"CurrencyCode":"USD",
"JournalBatchNumber":"90001",
"OffsetAccountDisplayValue":"",
"Text":"Test Line 2",
"TransDate":"2016-12-09",
"Voucher":"GNJL000101"
}
--changeset_boundary--
--batch_boundary--

After all of the above, my scratchpad looked like this:

image

7. Run the authorisation request
Select the request in Scratchpad and click Execute. Fiddler will switch to Inspector mode and show the response.

image

In the response, click the Raw tab.

image

You will see the successful OAuth response with token data in JSON format. Find the field “access_token” in JSON and copy its value – this is the value to be used in the OData requests. I have found the “View in Notepad” button very handy, to view the response as wrapped text and select/copy the needed part.

8. Replace the authorisation token in your OData request
Go back to Scratchpad and update the OData request you want to run, by inserting the authorisation token value after “Bearer”.

image

9. Run the request
Select the OData request in Scratchpad and click Execute. Inspect the result.

image

In this example, we’ll see HTTP response status 204, which is a success without any details (as it was a PATCH). Switching over to AX and looking up customer group T6, we can see that the customer group name has been updated.

That’s it!