Azure API Management : Exposing backend SOAP service as REST

A very common scenario for any API management tools is to expose an (older) back end SOAP service as a REST JSON service.
In this post I will explain how you can do this in Azure API Management. I'm not an API management expert, this is just my experience.

The service

The case I will be using is a WCF-basicHttp service hosted on a BizTalk 2010 machine and IIS. The service to list customers in the ERP (SAP).
The SOAP request and response look like this.

Request

<CustomerList xmlns="http://www.mydomain.com/schemas/">
 <SalesOrganizations>
  <SalesOrganization>XXXX</SalesOrganization>
 </SalesOrganizations>
 <WebshopRelevant>true</WebshopRelevant>
</myp:CustomerList>

Response

<CustomerListResponse xmlns="http://www.mydomain.com/schemas/">
 <Customer>
  <CustomerNumber>0000000001</CustomerNumber>
  <SalesOrganization>XXXX</SalesOrganization>
  <Lastname>Customer 1</Lastname>
 </Customer>
 <Customer>
  <CustomerNumber>0000000002</CustomerNumber>
  <SalesOrganization>XXXX</SalesOrganization>
  <Lastname>Customer 2</Lastname>
 </Customer>
</CustomerListResponse>

Import in Azure API management

Got to your API management resource on the Azure portal and click "API's" on the left.


Click "Add API" and select "WSDL" on the right.

In the next screen you have the option to browse to the WSDL or upload a WSDL file.
The most important thing is to select the "SOAP to REST" option. This will already make an XML to JSON mapping (and the other way around) for the request and response message.

Give it a name, choose an API URL suffix and press "Create".
Congratulations, you have exposed your back end service as a REST service on Azure API Management. The only issue now is that it still an HTTP POST, and we want to expose it as a GET.

Expose as GET

Go to your publisher portal (there is a link to both the publisher portal and developer portal on the azure portal)

On the publisher portal, go to APIs and click on your newly added API. Then go to the "Operations" tab and select the operation you want to change.
On this screen you can change your HTTP verb and change your URL template.
Change the HTTP verb to get and add any required parameters you like. In my case, I added the "salesorgs" parameter. Since my SOAP service allows multiple values, I will give the option to add a comma separated list. After this, go to the "Parameters" section on the left.

On this screen you can define all your parameters. Anything you want to add to the URL template parameters is required and has to be added to on the previous screen. You can add optional or required query parameters on the bottom part. In my case I added 1 optional parameter.

With this we already prepared the API to allow the parameters to be passed in this way. But now we still need to map these parameters to the XML input message. To be honest, that is the trickiest thing. I had to play around myself for some time to get it to work.

Transformation policy

The transformation is done in what they call a "Policy". You can actually do allot more in a policy, but in this post we are only focusing on the transformation part.
So go the the Policies section on the left. Select your API and operation and you will see your policy in a small screen.

The easiest way to edit this if you go to full screen mode. Press "configure policy" to start editing.
If you look at what is generated by default, it's already quite allot. In the "inbound" section you will see all the manipulations that will happen on an incoming request.

<inbound>
 <base />
 <set-body template="liquid">
  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
    <CustomerList xmlns="http://www.mydomain.com/schemas/">
     <SalesOrganizations>
{% assign salesOrgs = context.Request.OriginalUrl.Query.salesorgs[0] | Split: ',' %}
{% for item in salesOrgs %}
      <SalesOrganization>{{item}}</SalesOrganization>
{% endfor -%}
     </SalesOrganizations>
{% if context.Request.OriginalUrl.Query.wr[0].size&gt;0 %}
     <WebshopRelevant>{{context.Request.OriginalUrl.Query.wr[0]}}</WebshopRelevant>
{% endif %}
    </CustomerList>
   </soap:Body>
  </soap:Envelope>
 </set-body>
 <rewrite-uri template="/Customer.svc" copy-unmatched-params="false" />
 <set-header name="SOAPAction" exists-action="override">
  <value>"List"</value>
 </set-header>
 <set-header name="Content-Type" exists-action="override">
  <value>text/xml</value>
 </set-header>
 <set-method>POST</set-method>
</inbound>
The set-body section is where you will need to make the mapping to the XML input message. To do this, we need to use a template language called "liquid". There are other options, but this seems to be the easiest in this case. For more information you can go the Microsoft documentation https://docs.microsoft.com/en-us/azure/api-management/api-management-transformation-policies#SetBody

To get data from the URL you can use the context.Request.OriginalUrl.Query.salesorgs[0]. Just replace "salesorg" by your parameter name.
With liquid you can do some basic manipulations and checks. For the salesorgs parameter we need to use the Split (be careful, it's case sensitive) filter.
You can find a full reference off liquid here https://help.shopify.com/themes/liquid/basics
Just one thing to remember. In the policy the implementation of Liquid is in "C# mode". You will need to use Pascal casing for your filters.

Other things you will see in the policy are

  • rewrite-uri : any URL transformation you need to do. (the base URI is stored on another level)
  • set-header : in this case, sett's the SOAPAction header and content-type
  • set-method : this tells the system that we need to do a POST instead of a GET
The import wizard has already made a transformation for the response so we actually don't need to touch that. Unless you want to off course.

Testing the service

The easiest way to test what you did is to go to the developer portal. There is again a link on the azure portal.

Click on the API's tab and navigate to the operation you would like to test. When you click the try it button, you will get a screen that shows your parameters and lets you run a simple test.

When you click the send button on the bottom, your request will go through Azure API management to your back end service. You can see the response (in JSON) and see a full trace of what is happening in the back.
Now I'm getting this response
{
  "CustomerListResponse": [
    {
      "customerNumber": "0000000001",
      "salesOrganization": "XXXX",
      "lastname": "Customer 1",
    },
 {
      "customerNumber": "0000000002",
      "salesOrganization": "XXXX",
      "lastname": "Customer 2",
    }
  ]
}

Conclusion

When I was trying this out myself, I struggled allot with getting it to work. Mainly with the liquid template. The syntax is not well documented and it was difficult to find how I can access my URL parameters.
I think this is a very common use-case for Azure API management so I thought it would be helpful to create a simple guide on how to do it.
This is actually my first try at using Azure API management and except for the struggle with the liquid template, it actually went quite smooth. So I encourage you to just play around with it and try some things.

Comments

  1. Set-body documentation, just for you ;-) https://blogs.msdn.microsoft.com/apimanagement/2017/09/25/deep-dive-on-set-body-policy/

    ReplyDelete

Post a Comment

Popular posts from this blog

Query SAP HANA from BizTalk

Receive invalid xml through WCF adapter