Expose SOAP service in Azure API Management and store data in Blob
Tags: azure, soap, blob
Published: 2020-09-26 08:53:37

## Table of Contents
1 [Objective](#objective )  
2 [Create SOAP API](#createsoapapi)  
3 [Policy](#policy)  
4 [Request](#request )  
5 [Blob](#blob)

## 1 Objective <a id="objective "></a>

An external system has to send data but it's only capable to send SOAP messages. REST does not work for this system.  
API Management has to expose an endpoint to receive SOAP messages.  
For further processing, the entire SOAP message has to be stored in Azure Blob.

## 2 Create SOAP API <a id="createsoapapi"></a>

Create a new API in API Management by selecting WSDL.  
[Microsoft: Import SOAP API](https://docs.microsoft.com/en-us/azure/api-management/import-soap-api "Import SOAP API")


![APIM-NEW-WSDL.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1601019373662/C7V1aA3DL.png)

Provide a link to WSDL-Document or upload a WSDL-Document.
In my case, I uploaded a document:

![APIM-SOAP-WSDL.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1601018886832/zjM06MbIB.png)

I used the data contract for my previous post:  
[Transforming data from JSON to XML and XML to JSON with C#](https://markusmeyer.hashnode.dev/transforming-data-from-json-to-xml-and-xml-to-json "Transforming data from JSON to XML and XML to JSON with C#")

_DataContract:_
````C#
[DataContract(Name = "person")]
public class Person
{
    [DataMember(Name ="firstname")] 
    public string FirstName { get; set; }

    [DataMember(Name ="lastname")]
    public string LastName { get; set; }

    [DataMember(Name ="gender"), XmlAttribute]
    public string Gender { get; set; }

    [DataMember(Name ="age")]
    public int Age { get; set; }

}

```
The service itself contains a store method:

_ServiceContract:_
```C#
[ServiceContract]
public interface IPerson
{
    [OperationContract]
    void Store(PersonModel personModel);
}
```
By running the service on my machine, I saved the WSDL to the local disc.

## 3 Policy <a id="policy"></a>

The inbound policy contains a put request to Blob.
Most of the code is about authorization.
Please find more details about it in the [Microsoft documentation](https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key "Authorize with Shared Key").


The data will be stored in the container "soap".  

Named Values:

BLOB-URL: https://mmdatatransformation.blob.core.windows.net  

BLOB-KEY: secret  

````xml

<inbound>
    <base />
    <!-- ########## put to storage ########## -->
    <set-variable name="container" value="soap" />
    <set-variable name="fileName" value="@{return Guid.NewGuid().ToString() + ".xml";}" />
    <set-variable name="resource" value="@{
                        string prefix = "/" + context.Variables.GetValueOrDefault<string>("container") + "/";
                        string fileName = context.Variables.GetValueOrDefault<string>("fileName");
                        return prefix + fileName;
                        }" />
    <set-variable name="storageUrl" value="{{BLOB-URL}}" />
    <set-variable name="storageKey" value="{{BLOB-KEY}}" />
    <set-variable name="storageAccountName" value="@(context.Variables.GetValueOrDefault<string>("storageUrl").Split('.')[0].Split('/')[2])" />
    <set-variable name="blobUrl" value="@(context.Variables.GetValueOrDefault<string>("storageUrl") + context.Variables.GetValueOrDefault<string>("resource"))" />
    <set-variable name="date" value="@(DateTime.UtcNow.ToString("R"))" />
    <set-variable name="version" value="2018-03-28" />
    <set-backend-service base-url="@(context.Variables.GetValueOrDefault<string>("storageUrl") + "/" + context.Variables.GetValueOrDefault<string>("container"))" />
    <set-method>PUT</set-method>
    <set-header name="x-ms-date" exists-action="override">
        <value>@(context.Variables.GetValueOrDefault<string>("date") )</value>
    </set-header>
    <set-header name="x-ms-version" exists-action="override">
        <value>@(context.Variables.GetValueOrDefault<string>("version"))</value>
    </set-header>
    <set-header name="x-ms-blob-type" exists-action="override">
        <value>BlockBlob</value>
    </set-header>
    <set-header name="Content-Type" exists-action="override">
        <value>application/xml</value>
    </set-header>
    <set-header name="Authorization" exists-action="override">
        <value>@{
            string body = context.Request.Body.As<string>(preserveContent: true);
            string contentType = "application/xml";
            string contentLength = context.Request.Headers["Content-Length"][0];
            var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(context.Variables.GetValueOrDefault<string>("storageKey")) };
            var payLoad = string.Format("{0}\n\n\n{1}\n\n{2}\n\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-date:{3}\nx-ms-version:{4}\n{5}", 
                "PUT", 
                contentLength,
                contentType,
                context.Variables.GetValueOrDefault<string>("date"),
                context.Variables.GetValueOrDefault<string>("version"),
                "/" + context.Variables.GetValueOrDefault<string>("storageAccountName") + context.Variables.GetValueOrDefault<string>("resource"));
            return "SharedKey "+ context.Variables.GetValueOrDefault<string>("storageAccountName") + ":" + Convert.ToBase64String(hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad)));
        }</value>
    </set-header>
    <set-body>@( context.Request.Body.As<string>(true) )</set-body>
    <rewrite-uri template="@("/" + context.Variables.GetValueOrDefault<string>("fileName"))" copy-unmatched-params="true" />
</inbound>

```

## 4 Request <a id="request"></a>

My API contains one operation: Store

![apim_SOAP.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1601051788521/Mt25q1h8g.png)

With Postman a SOAP header has to be added:  
SOAPAction: http://tempuri.org/IPerson/Store

POST https://mm-sample.azure-api.net/person/

_Request Body:_
```xml
<?xml version="1.0" encoding="utf-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
      <Body>
        <Store xmlns="http://tempuri.org/">
          <person>
            <age>1</age>
            <firstname>firstname1</firstname>
            <gender>gender1</gender>
            <lastname>lastname1</lastname>
          </person>
        </Store>
      </Body>
</Envelope>
```
_Postman:_
![postman-soap-request.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1601050954749/wuQkyuz1w.png)

## 5 Blob <a id="blob"></a>

Finally, the SOAP data is stored in the blob and ready for further processing:


![soap_blob.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1601052152109/A3oQkbVW7.png)