O blog da AWS

Modernize seu servidor SOAP legado usando o Amazon API Gateway e o AWS Lambda – Parte 3

Por Daniel Abib, Enterprise Solution Architect, FSI – LATAM

 

Esta é a última parte (parte 3) da série de blog posts, aonde você aprenderá a construir uma solução de proxy para modernizar um cliente legado que utiliza o protocolo SOAP para comunicação com uma aplicação. Você pode analisar na parte 1 desta série de posts os conceitos principais do protocolo SOAP e a abordagem de como fazer a conversão para protocolos mais modernos como o REST usando o Amazon API Gateway e o AWS Lambda. Também poderá ver a na parte 2 a implementação de um proxy para um servidor SOAP legado.

Como apresentado na parte 1 do blog, finalizaremos nossa parte prática com a uma sugestão de implementação da arquitetura abaixo, mas, diferentemente do que foi realizado na parte 2, iremos agora implementar um proxy para uma aplicação cliente construída utilizando o protocolo SOAP e que precisa integrar com um servidor REST, conforme a arquitetura abaixo:

Vamos construir a solução usando o AWS SAM

Assim como detalhado na parte 2 da série de blog posts, para esta implementação, existem os seguintes pré-requisitos:

  • NodeJS 14.x ou superior (usaremos o NodeJS 16.x neste exemplo)
  • NPM (gerenciador de pacotes do NodeJS) versão 6x ou posterior
  • AWS SAM versão 1.53.0 ou posterior
  • Docker (é opcional, obrigatório apenas se você quiser testar sua API localmente)
  • curl – curl é uma ferramenta de linha de comando de código aberto para transferir dados e fazer requisições web usando vários protocolos de rede. Vamos usá-lo para testar nossa API localmente, bem como na AWS. Se você não estiver familiarizado com o curl, poderá usar o Postman, Insomnia, HTTPie, SoapUI ou outro software similar.

Vamos continuar usando o AWS Cloud 9 como um IDE, e se você não sabe como criar este ambiente de desenvolvimento virtual, há uma explicação nesse link.

Nossa abordagem consiste em 2 passos. Primeiramente, precisaremos criar um micro serviço que utiliza o protocolo REST (passo 1) que posteriormente será exposto através de um proxy utilizando o protocolo SOAP (passo 2). Esta primeira etapa de nossa arquitetura está ressaltada na imagem abaixo.

Após criarmos este serviço neste passo 1, iremos finalmente criar o processo de conversão de protocolos (SOAP para REST) utilizando a lógica necessária de conversão do header Content-type de “Text/xml” para “Application/Json” e também o payload de XML para JSON, que é a lógica que os clientes da AWS precisam efetivamente fazer para continuar utilizando as aplicações clientes feitas em SOAP, ressaltado abaixo como passo 2 a seguir:

Vamos começar criação do passo 1 realizando as seguintes atividades:

  1. Abra o serviço Cloud9 no console de gerenciamento da AWS (AWS console)
  2. No terminal ou usando os menus, crie uma pasta para o projeto chamada numberToWordsMicroservice com uma subpasta chamada ./src. A pasta ./src conterá o código Lambda necessário para esta solução.
mkdir numberToWordsMicroservice && cd numberToWordsMicroservice && mkdir src
 
       
    1. Crie os seguintes arquivos em branco:
        • ./template.yaml
       
        • ./src/app.js
       
        • ./src/package.json
         
   
touch template.yaml ./src/app.js ./src/package.json
 
       

A estrutura do projeto (pasta e arquivos) deve estar assim:

  1. Abra o arquivo template.yaml e copie o seguinte conteúdo no arquivo:
<pre><code class="lang-yaml"># © 2022 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.

# This AWS Content is provided subject to the terms of the AWS Customer Agreement
# available at http://aws.amazon.com/agreement or other written agreement between
# Customer and Amazon Web Services, Inc.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Microservice -&gt; NumberToWords

Globals:
  Function:
    Timeout: 3

Resources:
  NumberToWordsMicroServiceFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: src/
      Handler: app.handler
      Runtime: nodejs14.x
      Events:
        NumberToWords:
          Type: Api 
          Properties:
            Path: /
            Method: post

Outputs:
  NumberToWords:
    Description: "Test API Gateway endpoint URL for Prod stage for NumberToWords"
    Value: !Sub "curl -i -X POST https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/ -d '{\"data\": \"12\"}'"</code></pre>

Neste template do AWS SAM, estamos definindo uma função Lambda chamada NumberToWordsFunction (descrito a linha 16) e um REST API Gateway expondo o método /post (linhas 23 a 27). O código fonte da função Lambda será definido na pasta /src (linha 19), em um arquivo chamado app.js (linha 20), que conterá uma função chamada “handler”. Nas linhas 30 a 33 definimos as saídas (outputs) de nossa solução.

Lembre-se de salvar o arquivo.

  1. A próxima etapa é copiar o código fonte da nossa função Lambda. Abra o arquivo app.js na pasta ./src e copie o seguinte código:
// © 2022 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.

// This AWS Content is provided subject to the terms of the AWS Customer Agreement
// available at http://aws.amazon.com/agreement or other written agreement between
// Customer and Amazon Web Services, Inc.

var writtenNumberinWords = require('written-number');

exports.handler = async (event, context) => {
    let response;    
    
    const body = JSON.parse(event.body);
    const valueToConvert = body.data;
    
    try {
        let returnWords = writtenNumberinWords(valueToConvert);
        response = {'statusCode': 200, 'body': JSON.stringify({"message": returnWords})};
                    
    } catch (err) {
        return err;
    }
    
    return response;
};

Lembre-se de salvar o arquivo também.

Esta função do Lambda implementa um micro serviço muito simples, que recebe uma string que representa um número inteiro no formato JSON no campo “body.data” (linha 12).  Essa função faz  a decomposição (parse) deste valor para o formato numérico e utiliza uma biblioteca chamada writtenNumberinWords (linha 7) para converter o valor inteiro em sua descrição por extenso (em inglês), similar ao serviço do SOAP server descrito na parte 2 deste blog post.

De forma resumida, o que este código faz é, se você enviar um número inteiro como “548” no formato JSON, ele te retornará uma string contendo a informação “five hundred and forty-eigh“.

  1. Como estamos usando uma biblioteca externa no código Lambda, devemos atualizar o arquivo package.json e executar o comando “npm install” posteriormente. Abra na pasta ./src o arquivo package.json e copie o seguinte conteúdo:
{
  "name": "numberToWords",
  "version": "1.0.0",
  "description": "Microservice to convert a integer into ",
  "main": "app.js",
  "author": "Daniel Abib",
  "license": "MIT",
  "dependencies": {
    "written-number": "^0.11.1"
  }
}

  1. No terminal do Cloud9, altere o diretório para ./src e execute o seguinte comando para instalar as bibliotecas externas:
cd src && npm install

Você notará que existe 1 dependências e a pasta node_modules foi criada no diretório ./src para incluir essa biblioteca.

  1. Antes de testar o aplicativo, precisamos construir os artefatos necessários para a exeução do projeto usando o comando “sam build” no diretório raiz.
cd .. && sam build

O comando “sam build” processa o arquivo de template do AWS SAM, bem como o código fonte do aplicativo e quaisquer arquivos e dependências específicos da linguagem escolhida. O comando cria e copia artefatos no formato e local esperados (diretório .aws-sam) para as etapas subsequentes em seu fluxo de trabalho.

Você pode especificar dependências em um arquivo de manifesto que inclui em seu aplicativo, como requirements.txt para funções do Python ou package.json para funções do NodeJS.

A saída do comando “sam build” deve ser semelhante à imagem a seguir:

  1. Agora é hora de executar nosso aplicativo localmente para testá-lo. O AWS SAM permite que você execute seu aplicativo sem servidor (Serverless) localmente para desenvolvimento e teste rápidos. Quando você executa o comando a seguir em um diretório que contém suas funções e seu arquivo de template do AWS SAM, ele cria um servidor HTTP local (usando Docker) que hospeda todas as suas funções.

Você pode usar o comando “sam local start-api” para emular a execução de um Amazon API Gateway e receber a solicitação localmente, mas é obrigatório ter o docker rodando em seu ambiente local.

sam local start-api

**IMPORTANTE** Se você não tiver o docker em execução localmente em sua máquina, ignore esta etapa e implante o aplicativo conforme definido na continuação deste blog. No Cloud9, o docker está em execução por padrão.

Você pode abrir outro terminal (clique no botão + à direita de “guia Immediate” e escolha Novo Terminal) para mostrar a execução da chamada da API REST (método de solicitação POST usando curl) e manter o primeiro terminal aberto para mostrar como a o docker cria contêiner para executar o Amazon API Gateway e a função AWS Lambda.

  1. Você pode testar sua API local usando o seguinte comando curl (post):
curl -i -X POST localhost:3000/ -H "Content-Type: application/json" -d '{"data": "548"}'

A saída deve se parecer com a imagem a seguir:

O que construímos até aqui foi apenas um micro serviço que converte o número inteiro em seu descritivo extenso, utilizando o Amazon API Gateway como porta de entrada da requisição HTTP / REST e o AWS Lambda para executar a lógica simples de conversão.

Vamos publicar este micro serviço na AWS, pois precisaremos desta solução funcionando para que seja possível implementar o proxy de conversão de protocolos (SOAP para REST).

  1. Execute os seguintes passos:
  • Pressione Control+C no terminal que está executando o comando “sam local start-api”, use um segundo terminal ou abra um novo para executar o comando “sam deploy -g”
sam deploy -g
  • Preencha os seguintes valores quando solicitado:

Este comando (sam deploy -g) implanta seu aplicativo na nuvem da AWS. Ele pega os artefatos que foram criados, os empacota e os carrega em um bucket do Amazon Simple Storage Service (Amazon S3) que o AWS CloudFormation usará para criar o aplicativo.

Ao final da execução do sam deploy, você vera uma mensagem semelhante a imagem a seguir:

  1. Você pode usar o seguinte comando para testar sua solução diretamente na nuvem da AWS:
curl -i -X POST https://6f3wn533n0.execute-api.us-east-1.amazonaws.com/Prod/ -d '{"data": "1122"}'

(OBS1: você deve substituir a URL pela que você gerou na sua implantação)

(OBS2: você pode copiar o comando acima na sessão de saída do “sam deploy”)

Pronto, a primeira parte da nossa solução foi finalizada. Já temos o micro serviço que será utilizado para o processo de conversão de protocolos SOAP para REST, permitindo assim continuar utilizando o seu cliente SOAP legado em soluções que exponham suas interfaces em REST.

Passo 2

Agora que já temos um micro serviço em REST operando na nuvem da AWS, vamos começar segunda parte da solução, realizando as seguintes atividades para criar a arquitetura ressaltada abaixo:

  1. No terminal ou usando os menus, crie uma pasta para o projeto chamada proxySoapClient com uma subpasta chamada ./src. A pasta ./src conterá o código Lambda necessário para esta solução.
mkdir proxySoapClient && cd proxySoapClient && mkdir src
  1. Crie os seguintes arquivos em branco:
    • ./template.yaml
    • ./src/app.js
    • ./src/package.json
touch template.yaml ./src/app.js ./src/package.json
 
       

A estrutura do projeto (pasta e arquivos) deve estar assim:

  1. Abra o arquivo template.yaml e copie o seguinte conteúdo no arquivo:
# © 2022 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.

# This AWS Content is provided subject to the terms of the AWS Customer Agreement
# available at http://aws.amazon.com/agreement or other written agreement between
# Customer and Amazon Web Services, Inc.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: ProxySoapClient

Globals:
  Function:
    Timeout: 3
    Environment:
      Variables:
        NumberToWordMicroservice: !Ref URLForNumberToWordMicroservice
        
Parameters:
  URLForNumberToWordMicroservice:
    Type: String

Resources:
  ProxySoapClientWordsFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: src/
      Handler: app.handler
      Runtime: nodejs16.x
      Events:
        NumberToWords:
          Type: Api 
          Properties:
            Path: /
            Method: post
          Response:
            headers:
              Content-Type: "'text/xml'"

Outputs:

  LegacyIntegrationNumberToWords:
    Description: "To test the API Gateway endpoint URL for Prod stage for LegacyIntegrationNumberToWords"
    Value: !Sub "curl -i -X POST -H \"Content-Type: text/xml\"  https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/ --data @clientSoapEnvelope.xml"

A estrutura deste template do AWS SAM é muito similar as outras presente na parte 2 desta série de blog post e também do template apresentado no passo 1. A diferença principal é a necessidade de declararmos qual é a URL do serviço que estaremos fazendo o proxy.

Neste arquivo é solicitado o preenchimento desta URL no parâmetro URLForNumberToWordMicroservice, definido na linha 19. Na seção de parâmetros, os objetos declarados forçam a solicitação para o usuário de um prompt de entrada de parâmetros durante a execução do comando “deploy -g”. Para obter exemplos de parâmetros e os prompts correspondentes, consulte a documentação de referência de comandos da CLI.

Mais a diante iremos completar este parâmetro com a URL do micro serviço numberToWords que foi criado no item 12 do passo 1 deste blog.

O restante do arquivo de template do AWS SAM não deve ser muito diferente do que vocês já viram nesta sequencia de posts.

Lembre-se de salvar o arquivo.

  1. A próxima etapa é copiar o código-fonte da nossa função Lambda. Abra o arquivo app.js na pasta ./src e copie o seguinte código:
//  © 2022 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.

//  This AWS Content is provided subject to the terms of the AWS Customer Agreement
//  available at http://aws.amazon.com/agreement or other written agreement between
//  Customer and Amazon Web Services, Inc.

var parseString = require('xml2js').parseString;
const axios = require('axios');

exports.handler = async (event, context) => {
    // let response; 
    var valueNumber;
    
    try {
        
        parseString(event.body, function (err, result) {
            if (!err) {
                valueNumber = result['soap:Envelope']['soap:Body'][0]['ConvertNumberToWordsSoapIn'][0]['NumberToWordsRequest'][0];
            } else { 
                console.log (err);
                throw (err);
            }
        });

        var jsonObject = {
            "data" : valueNumber
        };
        
        const headers = { 
            'Content-Type': 'Application/json'
        };
        
        var resultNumberToWords = await axios.post(process.env.NumberToWordMicroservice, jsonObject, { headers });
        
        var resp =  create_response (JSON.stringify(resultNumberToWords.data.message));

        let response = {'statusCode': 200, 
                        headers: {"content-type": "text/xml"}, 
                        'body': JSON.stringify(resp)
        };
        
        return response;
        
    } catch (err) {
        console.log ("Error: " + err);
        let response = {'statusCode': 500, headers: {"content-type": "text/xml"}, 'body': err};
        return response;
    }
};

function create_response(numberInWords) {
  return '<?xml version="1.0" encoding="utf-8"?> \
            <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">\
            <soap:Body>\
              <m:ConvertNumberToWordsResponse xmlns:m="http://www.dataaccess.com/webservicesserver/"> \
                  <m:ConvertNumberToWordsResponseResult>' + numberInWords + '</m:ConvertNumberToWordsResponseResult> \
              </m:ConvertNumberToWordsResponse> \
            </soap:Body>\
            </soap:Envelope>';
}

Lembre-se de salvar o arquivo também.

De forma análoga como implementado na parte 2 deste blog, esta função Lambda tem 4 ações principais.

A primeira é receber da aplicação cliente o payload no formato XML (mandatório para o protocolo SOAP) e converter para o formato JSON (representado nas linhas 16 a 27).

A segunda parte é converter o header de Content-Type de “Text/XML” (também obrigatório no protocolo SOAP) para “Application/JSON” (linhas 29 a 31).

A terceira parte é a chamada do micro serviço numberToWords através de uma chamada HTTP passando o payload já em formato JSON e o header de Content-Type já convertido (linha 33).

A ultima parte é a montagem do XML de retorno, esperado pelo protocolo SOAP, contento os dados convertidos da aplicação REST numberToWords (linha 35). Para este passo, foi criado uma função auxiliar de criação da estrutura do XML esperada pelo cliente SOAP.

  1. Como estamos usando bibliotecas externas no código Lambda, devemos atualizar o arquivo package.json e executar o comando “npm install” posteriormente. Abra na pasta ./src o arquivo package.json e copie o seguinte conteúdo:
{
  "name": "ProxySoapClient",
  "version": "1.0.0",
  "description": "Applicatio to proxy a SOAP client request into REST",
  "main": "app.js",
  "author": "Daniel Abib",
  "license": "MIT",
  "dependencies": {
    "axios": "^0.21.4",
    "xml2js": "^0.4.23"
  }
}

  1. No terminal do Cloud9, altere o diretório para ./src e execute o seguinte comando para instalar as bibliotecas externas:
cd src && npm install && cd ..

Você notará que existem 2 dependências e a pasta node_modules foi criada no ./src para incluir essas bibliotecas.

  1. Antes de testar o aplicativo, precisamos construir os artefatos usando o comando “sam build” no diretório raiz do projeto.
cd .. && sam build

O comando “sam build” processa o arquivo de template do AWS SAM bem como o código fonte do aplicativo e quaisquer arquivos e dependências específicos da linguagem escolhida. O comando cria e copia artefatos no formato e local esperados (diretório .aws-sam) para as etapas subsequentes em seu fluxo de trabalho.

Você pode especificar dependências em um arquivo de manifesto que inclui em seu aplicativo, como requirements.txt para funções do Python ou package.json para funções do NodeJS.

A saída do comando “sam build” deve ser semelhante à imagem a seguir:

  1. Agora é hora de executar nosso aplicativo localmente para testá-lo. O AWS SAM permite que você execute seu aplicativo sem servidor (Serverless) localmente para desenvolvimento e teste rápidos. Quando você executa o comando a seguir em um diretório que contém suas funções e seu template do AWS SAM, ele cria um servidor HTTP local (usando Docker) que hospeda todas as suas funções.

Como o cliente SOAP precisa de um XML contendo os dados do request no formato SoapEnvelope para o servidor, vamos criar um arquivo local com a estrutura do payload que será enviado para nosso proxy. Para isso, crie um arquivo e copie o seguinte conteúdo dentro dele.

touch clientSoapEnvelope.xml

Você pode usar o comando “sam local start-api” para emular a execução de um Amazon API Gateway e receber a solicitação localmente, mas é obrigatório ter o docker rodando em seu ambiente local.

Diferentemente do que já foi apresentado neste blog post, como nossa aplicação precisa saber qual a URL do micro serviço numberToWords que simula nosso servidor REST, precisamos passar um parâmetro a mais no comando sam local start-api.

sam local start-api --parameter-overrides URLForNumberToWordMicroservice=https://6f3wn533n0.execute-api.us-east-1.amazonaws.com/Prod/

Lembrem-se de substituir a URL do endpoint do micro serviço numberToWords por aquela gerada no passo 12 deste blog post.

**IMPORTANTE** Se você não tiver o docker em execução localmente em sua máquina, ignore esta etapa e implante o aplicativo conforme definido na continuação deste blog. No Cloud9, o docker está em execução por padrão.

Você pode abrir outro terminal (clique no botão + à direita de “guia Immediate” e escolha Novo Terminal) para mostrar a execução da chamada da API REST (método de solicitação POST usando curl) e manter o primeiro terminal aberto para mostrar como a o docker cria contêiner para executar o Amazon API Gateway e a função Lambda.

  1. Você pode testar sua API local usando o seguinte comando curl (post):
curl -i -X POST -H 'Content-type: text/xml'  -d @clientSoapEnvelope.xml http://localhost:3000

(OBS: Lembre-se de mudar para o diretório aonde contem o arquivo clientSoapEnvelope.xml executando o comando cd proxySoapClient)

A saída deve se parecer com a imagem a seguir:

O resultado da chamada do cliente (curl) é um XML contendo a resposta SOAP, com uma tag que contem o resultado “one thousand five hundred and nineteen”, valor este que foi passado no arquivo de request em formato de número.

Para finalizar nossa solução, vamos publicar esta solução (Proxy SOAP para REST) na AWS, e assim completamos os 2 passos de criar um micro serviço REST chamado numberToWords e uma solução de Proxy SOAP para REST que permite uma aplicação enviar uma solicitação com um payload XML e um header Content-Type “Text/XML” e ser respondido por um servidor REST após todas as conversões de formato.

  1. Execute os seguintes passos:
  • Pressione Control+C no terminal que está executando o comando “sam local start-api”, use um segundo terminal ou abra um novo para executar o comando “sam deploy -g”
sam deploy -g
  • Preencha os seguintes valores quando solicitado:

Vale ressaltar, conforme mencionado no passo 20, que um parâmetro adicional é esperado no processo de implantação (deploy). Precisamos informar para a o AWS SAM qual a URL do micro serviço numberToWord, para que ela seja utilizada no código Lambda e chame este serviço de forma adequada. A seta em vermelho da figura acima representa o este valor, e deve ser substituída pela gerada no passo 12 deste blog post.

Este comando (sam deploy -g) implanta seu aplicativo na nuvem da AWS. Ele pega os artefatos que foram criados, os empacota e os carrega em um bucket do Amazon Simple Storage Service (Amazon S3) que o AWS CloudFormation usará para criar o aplicativo.

  1. Você pode usar o seguinte comando para testar sua solução:
curl -i -X POST -H "Content-Type: text/xml"  https://zes5gb8xxl.execute-api.us-east-1.amazonaws.com/Prod/ --data  @clientSoapEnvelope.xml

(OBS1: você deve substituir a URL pela que você gerou na sua implantação)

(OBS2: você pode copiar o comando acima na sessão de saída do “sam deploy”)

Por mais que tenhamos que fazer vários passos, a complexidade do processo de conversão de protocolos foi facilitada pela utilização do Amazon API Gateway e também pela facilidade de criação da função lambda.

Devem ter reparado que em nenhum momento precisamos criar, configurar ou administrar servidores, porque todos os serviços apresentados aqui são Serverless.

Limpar o workload que foi gerado

Usando o serviço AWS Serverless, os clientes pagam apenas pelo que usam. Se não houver solicitação para AWS Lambda ou Amazon API Gateway, você não será cobrado.

Ambos os serviços usados ​​aqui têm um nível gratuito conforme definido abaixo:

Serviço Nível gratuito
Amazon API Gateway O nível gratuito do Amazon API Gateway inclui um milhão de chamadas de API recebidas para APIs REST, um milhão de chamadas de API recebidas para APIs HTTP e um milhão de mensagens e 750.000 minutos de conexão para APIs WebSocket por mês por até 12 meses.
AWS Lambda O nível gratuito do AWS Lambda inclui um milhão de solicitações gratuitas por mês e 400.000 GB-segundos de tempo de computação por mês, utilizáveis ​​para funções com processadores x86 e Graviton2, em conjunto.

Mas, se você quiser limpar sua conta da AWS, basta executar um comando do AWS SAM:

sam delete

Você precisa confirmar a exclusão do aplicativo, bem como os artefatos armazenados no S3. Este será o resultado:

Se você tiver outros casos de uso para modernizar arquiteturas legadas SOAP e quiser compartilhá-lo comigo, entre em contato comigo no LinkedIn e tentarei ajudar. https://www.linkedin.com/in/danielabib/

 


Sobre o autor

Daniel Abib é Enterprise Solution Architect na AWS, com mais de 25 anos trabalhando com gerenciamento de projetos, arquiteturas de soluções escaláveis, desenvolvimento de sistemas e CI/CD, microsserviços, Serverless e segurança. Ele trabalha apoiando clientes corporativos, ajudando-os em sua jornada para a nuvem.