Azure API Management Soap Facade | pass through

2016, Aug 05    

When you’ve got an old web service that’s needing to be consumed by a mobile app (which loves binding json) – but you can’t change the code of your webservice, you’ve got 1 real option available. Create a façade for the web service.
The main benefit is of course that the mobile app can consume the json directly, without conversion. But API Management also gives you the ability to manage multiple Api’s, protect the API’s (security/throttling), monitor/monetise the API’s and more importantly introduce a caching layer.

Here’s a great guide that a fellow Softie’s written, which takes you through the whole process with a great explanation.
http://houghtonweb.co.uk/index.php/2016/06/01/using-azure-api-management-to-convert-soap-api-xml-to-json/

To take it a little further, if you don’t want to submit a SOAP-esque json docment then you need to do a little more policy authoring. My service doesn’t require anything in the request body, just 2 querystring parameters.

The key parts in the policy below are;

  • Wrap the SOAP envelope in CDATA tags
  • Use tokens in your SOAP packet, and replace them after setting the request body
  • Set the header to text/xml after setting the body
  • Use an Eventhub to debug the new body of the request you’re making. This site is really helpful in getting the Eventhub working with APIM
  • Convert the response to Json
  • Consider replacing some of the response Soap envelope tags and xml namespace tags
<policies>
	<inbound>
		<base />
		<rewrite-uri template="/Shopping/v2/" />
		<set-body><![CDATA[<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:shop="http://somewebservice.net/ws/shopping" xmlns:com="http://somewebservice.net/ws/commontypes">
   <soapenv:Header/>
   <soapenv:Body>
      <shop:pointToPointShoppingRequest>
         <com:context>
            <com:distributorCode>DEMO</com:distributorCode>
            <com:pointOfSaleCode>GB</com:pointOfSaleCode>
            <com:channelCode>CON</com:channelCode>
         </com:context>
         <shop:pointToPointShoppingQuery fareFilter="CHEAPEST_ONEWAY_AND_ROUNDTRIP">
            <shop:travelPointPairs>
               <shop:travelPointPair>
                  <com:originTravelPoint type="STATION">GBRDG</com:originTravelPoint>
                  <com:destinationTravelPoint type="STATION">GBQQM</com:destinationTravelPoint>
                  <com:departureDateTimeWindow>
                     <com:date>[tokenDate]</com:date>
                     <com:time>[tokenTime]</com:time>
                  </com:departureDateTimeWindow>
               </shop:travelPointPair>
            </shop:travelPointPairs>
            <shop:passengerSpecs>
               <shop:passengerSpec passengerSpecID="PAX_A1">
                  <com:age>40</com:age>
               </shop:passengerSpec>
            </shop:passengerSpecs>
         </shop:pointToPointShoppingQuery>
      </shop:pointToPointShoppingRequest>
   </soapenv:Body>
</soapenv:Envelope>]]></set-body>
		<find-and-replace from="[tokenDate]" to="@(context.Request.Url.Query.GetValueOrDefault("date"))" />
		<find-and-replace from="[tokenTime]" to="@(context.Request.Url.Query.GetValueOrDefault("time"))" />
		<set-header exists-action="override" name="content-type">
			<value>text/xml</value>
		</set-header>
		<log-to-eventhub logger-id="debuglogger"> @( string.Join("##", DateTime.UtcNow, context.Deployment.ServiceName, context.User.Email, context.Deployment.Region, context.RequestId, context.Request.IpAddress, context.Operation.Name, "Request", context.Request.Body.As<string>(preserveContent: true))) </log-to-eventhub>
	</inbound>
	<backend>
		<base />
	</backend>
	<outbound>
		<xml-to-json kind="direct" apply="always" consider-accept-header="false" />
		<find-and-replace from="soap$Envelope" to="ContosoResults" />
		<find-and-replace from="ns2$" to="" />
		<find-and-replace from="soap$" to="" />
	</outbound>
	<on-error>
		<base />
	</on-error>
</policies>