SOAP Pass-Through via API Management

Before diving into the solution, I will first explain the problem statement at hand. I wanted to call the AFAS OnLine SOAP Service to get employee data. First I started in SOAPUI. I could call the service both via SOAP 1.1 and SOAP 1.2: Address: (with 99999 replaced by a customer specific access code) Request: <Envelope xmlns=””> <Body> <GetData xmlns=”urn:Afas.Profit.Services”> <token><![CDATA[<token><version>1</version><data>CAAA6BD…FFF1E9</data></token>]]></token> <connectorId>Medewerker_indienst_Hak</connectorId> <skip>0</skip> <take>2</take> </GetData> </Body> </Envelope> As you can see, I have to pass a token element to authorize the client. That’s a secret code I don’t want to share with external IT partners. So, that’s how I came to the idea to use Azure API Management and use the Set-Body policy. It’s quite easy to import a WSDL via Azure API Management, so I’ll skip that explanation. The problem comes when you want to test the new API. You will notice, API Management hasn’t imported all headers. That’s a limitation of the WSDL import in API Management. The solution is to add set-header statements to the inbound policy. Next issue. You can replace the value of a SOAP Header (like Content-Type in the example below), but you can’t add a header (like SOAPAction in the example below). That means, you will have to add the SOAPAction header by hand when using the Test facility of API Management. Unfortunately, I later found out you can’t replace the value of the SOAPAction header at all. Maybe that’s because it’s a SOAP header, not a Http header. Really searched for a solution, but I’m afraid that’s another limitation. Good thing is, the body of the SOAP request can be set via a policy. That way, I am able to pass a token without expecting from the external IT partner to pass one. Nice! Also note that we get the connectorId, skip and trace settings from the URL query string <inbound> <base /> <set-header name=”Content-Type” exists-action=”override”> <value>text/xml;charset=UTF-8</value> </set-header> <set-body template=”liquid”> <Envelope xmlns=””> <Body> <GetData xmlns=”urn:Afas.Profit.Services”> <token><![CDATA[<token><version>1</version><data>CAAA6BD…FFF1E9</data></token>]]></token> <connectorId>{{context.Request.OriginalUrl.Query.connectorId}}</connectorId> <skip>{{context.Request.OriginalUrl.Query.skip}}</skip> <take>{{context.Request.OriginalUrl.Query.take}}</take> </GetData> </Body> </Envelope> </set-body> <set-header name=”SOAPAction” exists-action=”override”> <value>urn:Afas.Profit.Services/GetData</value> </set-header> </inbound> To test the service operation with the query parameters, below you will find the test screen from the Publisher portal: Now there’s one problem left. I don’t know if the service that I exposed via API Management is actually a SOAP Service. That was a requirement: the external IT partner could only call SOAP services, not REST services. As you may have noticed, you can’t retrieve the WSDL via svc?wsdl or asmx?wsdl, so … I found out you can go to the Developer Portal. Navigate to the API definition and you will find an [API Definition] drop-down button in the top-right corner. Select WSDL from the drop-down and you will get the URL. Next we can call the service from c#. Since we have to perform a Http Post, we can use the Http Client:\ string serviceAddress = “”; var client = new HttpClient(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, serviceAddress); request.Headers.TryAddWithoutValidation(“Ocp-Apim-Subscription-Key”, “7fae5967c9c94106a5204c59f67aac35”); request.Headers.TryAddWithoutValidation(“SOAPAction”, “urn:Afas.Profit.Services/GetData”); request.Headers.TryAddWithoutValidation(“Content-Type”, “text/xml;charset=UTF-8”); request.Content = new StringContent(“<Envelope xmlns=\”\”><Body><GetData xmlns=\”urn:Afas.Profit.Services\”><token><![CDATA[<token><version>1</version><data>CAAA6BD…FFF1E9</data></token>]]></token><connectorId>Medewerker_indienst_Hak</connectorId><skip>0</skip><take>2</take></GetData></Body></Envelope>”); var response = client.SendAsync(request); if (response.Result.IsSuccessStatusCode) { var responseContent = response.Result.Content.ReadAsStringAsync(); }