In this series we are going to look at how to set up the Royal Mail SOAP Shipping API with AX 2012 R2. I will primarily use X++ code to connect to the Royal Mail client proxy, however some C# WCF code will be needed to set the correct SOAP security headers and log incoming and outgoing messages.
Setting up the Royal Mail API Account
Your company will need a contract with Royal Mail and you will need to go through their onboarding process, as part of these you will need to create a new account on their developer website
Then register your application that will use the API. In the long run you will want at least 2 applications one for live and one for test/development. For the moment just set up a test one.
You will need to have the 10 character Royal Mail account number they gave you when you registered with them.
You will receive a client ID and Secret when you register the application you will need these so keep them somewhere safe but retrievable.
Testing the API in the Portal
Go to the API Portal and choose Shipping API (SOAP). Here you can see all the operations you can use and examples in different programming languages of how to call the API and what the resulting request and response messages look like.
Here test that your credentials work properly, by finding the Try this Operation section on the right hand side console like window and entering your Client Secret.
This is also a good place to return to when you want to quickly check that the requests you are sending from your application are valid and will work with the API.
You will also find the technical and user documentation for this API, which are well worth reading to make sure you are sending valid data.
Set up the WCF Project
Thank you to Mohamed Mahmoud El-Geish getting me started on this solution
Create a new C# Library Project and add a service reference to https://api.royalmail.net/shipping/v2 calling it RoyalMail
In the app.config make sure the basicHttpBinding uses Transport security mode and that the maxReceivedMessageSize, maxBufferSize and maxBufferPoolSize are something large (I used 20000000).
We want to do 2 things with this project
- Add some HTTP Request Headers
- Add logging of the Request and Response messages
Lets start with the adding a class that implements IClientMessageInspector
This Message Inspector will be added to a MessageInspector collection and is used to inspect or modify messages passing through the WCF proxy.
The interface has 2 methods
We will use BeforeSendRequest to
- Set the HTTPMessagePropertyHeaders
We will pass the credentials to the ClientMessageInspector object from AX.
Then we check if there is a HttpRequestMessageProperty which holds the message headers. If this object does not exist we can create it and added it to the Request properties.
In the SetHeaders function we need to set the following:
- add The Accept Header, setting this to application/soap+xml
- add the Royal Mail API Client Id
- add the Royal Mail API Secret
2. Log the Request Message
For debugging it is going to be useful in production to be able to view what messages have been sent and received. To avoid the logging code causing exceptions I have wrapped it in a try/catch and set it to silently fail.
For testing we can use something like Fiddler to check the request and response messages.
I also set a GUID, which we can use to identify the relationship between the Request and Response message.
Finally in this method we will just log the response message adding the CorrelationState which is the Guid we set in BeforeSendRequest.
This will be added to the response message log allowing a way to link back to the original request message.
|/// Used – https://blogs.msdn.microsoft.com/mohamedg/2012/12/13/adding-http-headers-to-wcf-calls/|
|public class ClientMessageInspector : IClientMessageInspector|
|public void SetCredentials(RoyalMailHeaderCredentials credentials)|
|_credentials = credentials;|
|public object BeforeSendRequest(ref Message request, IClientChannel channel)|
|var guid = Guid.NewGuid();|
|RequestMessage requestMessage = new RequestMessage(guid);|
|public void AfterReceiveReply(ref Message reply, object correlationState)|
|ResponseMessage responseMessage = new ResponseMessage((Guid)correlationState);|
|private void SetMessagePropertyHeaders(Message request)|
|if (_credentials == null) return;|
|HttpRequestMessageProperty.Name, out object httpRequestMessageObject))|
|if (!(httpRequestMessageObject is HttpRequestMessageProperty property))|
|throw new ArgumentNullException(nameof(property));|
|HttpRequestMessageProperty property = new HttpRequestMessageProperty();|
|private void SetHeaders(HttpRequestMessageProperty property)|
|property.Headers["Accept"] = "application/soap+xml";|
|property.Headers["X-IBM-Client-Id"] = _credentials.ClientID;|
|property.Headers["X-IBM-Client-Secret"] = _credentials.Secret;|
In the next Post
We will add classes to write the request and response messages out to file for logging.