Usando SOAP-to-REST para Simplificar Migração e Integração
Jinhua Luo
March 31, 2023
1. O que é um Web Service?
Um web service é definido pelo World Wide Web Consortium (W3C) como um sistema de software projetado para suportar interação interoperável entre máquinas em uma rede.
Um web service executa tarefas específicas ou um conjunto de tarefas e é descrito por uma representação XML padrão de um serviço chamada Web Services Description Language (WSDL). A descrição do serviço fornece todos os detalhes necessários para interagir com o serviço, incluindo formatos de mensagens (usados para descrever operações em detalhes), protocolos de transporte e localização.
Outros sistemas interagem com o web service usando mensagens SOAP, geralmente por meio de HTTP em conjunto com serialização XML e outros padrões relacionados à web.
O diagrama de arquitetura de um web service (note que o service broker é opcional) é:
*Fonte da imagem (licenciada sob CC 3.0 BY-SA): https://en.wikipedia.org/wiki/Web_service
A interface WSDL oculta informações detalhadas sobre como o serviço é implementado, de modo que o uso do serviço é independente da plataforma de hardware ou software que implementa o serviço, bem como da linguagem de programação usada para escrever o serviço.
Aplicações baseadas em web services são fracamente acopladas, orientadas a componentes e implementações de tecnologia cruzada. Web services podem ser usados individualmente ou com outros web services para realizar agregações complexas ou transações de negócios.
Web services são as unidades de implementação da Arquitetura Orientada a Serviços (SOA), um método de design usado para substituir sistemas monolíticos. Um sistema extenso pode ser dividido em vários web services, que podem então ser combinados em uma grande caixa preta para fornecer lógica de negócios externamente. Os populares microsserviços baseados em contêineres são a substituição mais recente dos web services. No entanto, muitos sistemas legados já são baseados em web services para implementação e operação, portanto, a compatibilidade com esses sistemas continua sendo um desafio, apesar da rápida evolução da tecnologia.
WSDL (Web Services Description Language)
WSDL é uma notação XML usada para descrever web services. A definição WSDL instrui os clientes sobre como escrever solicitações de web services e define a interface fornecida pelo provedor do web service.
A definição WSDL é dividida em várias partes, especificando a interface lógica do web service e detalhes físicos. Detalhes físicos incluem informações de endpoint, como o número da porta HTTP, bem como informações de binding que especificam como representar o conteúdo da mensagem SOAP e qual método de transporte usar.
Fonte da imagem (licenciada sob CC 3.0 BY-SA): https://en.wikipedia.org/wiki/Web_Services_Description_Language
- Um arquivo WSDL pode conter vários serviços.
- Um serviço pode conter várias portas.
- Uma porta define um endereço URL (que pode variar para cada porta) e pode conter várias operações.
- Cada operação inclui um tipo de entrada e um tipo de saída.
- Tipos definem a estrutura da mensagem, incluindo quais campos compõem a mensagem, o tipo de dados de cada campo (que pode ser aninhado) e o número de campos permitidos.
1.1 O que é SOAP
SOAP é um formato de mensagem XML usado em interações de web services. As mensagens SOAP são geralmente enviadas via HTTP ou JMS, mas outros protocolos de transporte também podem ser usados. A definição WSDL descreve o uso do SOAP em um web service específico.
Existem duas versões comumente usadas do SOAP: SOAP 1.1 e SOAP 1.2.
Fonte da imagem (licenciada sob CC 3.0 BY-SA): https://en.wikipedia.org/wiki/SOAP
Uma mensagem SOAP contém as seguintes partes:
- Metadados do cabeçalho, que geralmente estão vazios
- Corpo
- O tipo de mensagem definido no WSDL
- Além de respostas bem-sucedidas, também há mensagens de erro estruturadas para tipos de resposta.
Por exemplo:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header></SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns2:getCountryResponse xmlns:ns2="http://spring.io/guides/gs-producing-web-service">
<ns2:country>
<ns2:name>Spain</ns2:name>
<ns2:population>46704314</ns2:population>
<ns2:capital>Madrid</ns2:capital>
<ns2:currency>EUR</ns2:currency>
</ns2:country>
</ns2:getCountryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
1.2 O que é REST
Um web service é um conceito abstrato que pode ser implementado de qualquer maneira; por exemplo, REST é um método de implementação popular.
REST, abreviação de Representational State Transfer, literalmente significa transferência de estado da camada de apresentação. Roy Thomas Fielding propôs o termo REST em sua tese de doutorado em 2000. Na época, a internet estava em expansão, e havia a necessidade de uma definição prática de desenvolvimento de software e interação com redes.
Por muito tempo, a pesquisa de software focou principalmente na classificação do design de software e na evolução dos métodos de design. Raramente avaliava objetivamente o impacto de diferentes escolhas de design no comportamento do sistema. Por outro lado, a pesquisa de rede focava principalmente nos detalhes do comportamento de comunicação entre sistemas e como melhorar o desempenho de mecanismos de comunicação específicos, muitas vezes negligenciando que mudar o estilo de interação da aplicação tem um impacto maior no desempenho geral do que mudar o protocolo de interação. Meu artigo visa entender e avaliar o design de arquitetura de software de aplicação baseado em rede que é funcional, tem bom desempenho e é adequado para comunicação, enquanto cumpre os princípios de arquitetura.
Acessar um site representa um processo de interação entre o cliente e o servidor. Durante esse processo, deve haver mudanças nos dados e no estado. O protocolo HTTP é sem estado, o que significa que todos os estados são armazenados no lado do servidor. Portanto, se o cliente quiser operar o servidor, ele deve usar algum meio para causar "transferência de estado" no lado do servidor. Como essa transferência é construída na camada de apresentação, é chamada de "transferência de estado da camada de apresentação."
Os quatro princípios básicos do REST são:
- Use verbos HTTP: GET, POST, PUT, DELETE;
- Conexão sem estado, o servidor não deve armazenar muito estado contextual; ou seja, cada solicitação é independente;
- Defina um URI para cada recurso;
- Use
x-www-form-urlencoded
ou JSON como formato de dados;
Converter SOAP para REST pode facilitar o acesso dos usuários a web services tradicionais de maneira RESTful, reduzindo o custo de desenvolvimento de clientes SOAP. Além disso, se puder se adaptar dinamicamente a qualquer web service com desenvolvimento de código zero, seria ainda mais perfeito.
A maior vantagem do REST é que ele não tem esquema, tornando o desenvolvimento conveniente, e o JSON tem maior legibilidade e menor redundância.
2. Implementações Tradicionais de Proxy SOAP-to-REST
2.1 Conversão Manual de Modelo
Essa abordagem requer a criação de modelos de conversão para a solicitação e resposta de cada operação do web service, o que também é usado por muitos outros produtos de gateway.
Podemos usar o plugin de transformação de corpo do APISIX para criar um proxy simples de SOAP para REST e tentar essa abordagem.
Por exemplo, construímos um modelo de solicitação formatado em XML para a operação getCountry
do CountriesPortService
no arquivo WSDL com base na definição de tipo.
Aqui, preenchemos o campo name
no JSON no campo name
no getCountryRequest
.
req_template=$(cat <<EOF | awk '{gsub(/"/,"\\\"");};1' | awk '{$1=$1};1' | tr -d '\r\n'
<?xml version="1.0"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Body>
<ns0:getCountryRequest xmlns:ns0="http://spring.io/guides/gs-producing-web-service">
<ns0:name>{{_escape_xml(name)}}</ns0:name>
</ns0:getCountryRequest>
</soap-env:Body>
</soap-env:Envelope>
EOF
)
Um modelo de XML para JSON precisa ser fornecido para a resposta, o que pode ser um pouco mais complexo (especialmente se diferenças de versão do SOAP precisarem ser consideradas). Isso porque é necessário determinar se a resposta foi bem-sucedida:
- Para uma resposta bem-sucedida, os campos podem ser mapeados diretamente para JSON.
- Para uma resposta com falha (ou seja, um fault), uma estrutura JSON separada é necessária, e deve ser determinado se campos opcionais específicos estão presentes.
rsp_template=$(cat <<EOF | awk '{gsub(/"/,"\\\"");};1' | awk '{$1=$1};1' | tr -d '\r\n'
{% if Envelope.Body.Fault == nil then %}
{
"currency":"{{Envelope.Body.getCountryResponse.country.currency}}",
"population":{{Envelope.Body.getCountryResponse.country.population}},
"capital":"{{Envelope.Body.getCountryResponse.country.capital}}",
"name":"{{Envelope.Body.getCountryResponse.country.name}}"
}
{% else %}
{
"message":{*_escape_json(Envelope.Body.Fault.faultstring[1])*},
"code":"{{Envelope.Body.Fault.faultcode}}"
{% if Envelope.Body.Fault.faultactor ~= nil then %}
, "actor":"{{Envelope.Body.Fault.faultactor}}"
{% end %}
}
{% end %}
EOF
)
Configure o roteamento do APISIX e realize o teste:
curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: xxx' -X PUT -d '
{
"methods": ["POST"],
"uri": "/ws/getCountry",
"plugins": {
"body-transformer": {
"request": {
"template": "'"$req_template"'"
},
"response": {
"template": "'"$rsp_template"'"
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"localhost:8080": 1
}
}
}'
curl -s http://127.0.0.1:9080/ws/getCountry \
-H 'content-type: application/json' \
-X POST -d '{"name": "Spain"}' | jq
{
"currency": "EUR",
"population": 46704314,
"capital": "Madrid",
"name": "Spain"
}
# Resposta de falha
{
"message": "Your name is required.",
"code": "SOAP-ENV:Server"
}
Como podemos ver, esse método requer uma compreensão manual da definição de cada operação no arquivo WSDL e do endereço do web service correspondente a cada operação. Se o arquivo WSDL for grande, contiver um grande número de operações e tiver definições de tipo aninhadas complexas, essa abordagem pode ser muito trabalhosa, difícil de depurar e propensa a erros.
2.2 Apache Camel
Camel é um conhecido framework de integração Java usado para implementar pipelines de roteamento que convertem diferentes protocolos e lógicas de negócios, e SOAP-to-REST é apenas um de seus casos de uso.
Usar o Camel requer baixar e importar o arquivo WSDL, gerar código stub para o cliente SOAP e escrever código Java:
- Definir endpoints REST
- Definir rotas de conversão de protocolo, como campos JSON mapeiam para campos SOAP
Como exemplo, vamos considerar um web service para conversão de unidades de temperatura:
https://apps.learnwebservices.com/services/tempconverter?wsdl
- Gerar código de cliente SOAP com base no arquivo WSDL usando Maven.
O cxf-codegen-plugin
gerará endpoints de cliente SOAP para acessar o web service.
<build>
<plugins>
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<wsdlOptions>
<wsdlOption>
<wsdl>src/main/resources/TempConverter.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
- Escrever o bean do cliente SOAP.
Observe que lembramos o nome do bean como cxfConvertTemp
, que será usado ao definir as rotas do Camel posteriormente.
import com.learnwebservices.services.tempconverter.TempConverterEndpoint;
@Configuration
public class CxfBeans {
@Value("${endpoint.wsdl}")
private String SOAP_URL;
@Bean(name = "cxfConvertTemp")
public CxfEndpoint buildCxfEndpoint() {
CxfEndpoint cxf = new CxfEndpoint();
cxf.setAddress(SOAP_URL);
cxf.setServiceClass(TempConverterEndpoint.class);
return cxf;
}
}
- Escrever a rota REST downstream primeiro.
A partir dessa rota, podemos ver que ela define URLs no estilo RESTFul e suas definições de parâmetros e define a próxima rota para cada URL. Por exemplo, /convert/celsius/to/fahrenheit/{num}
pega a última parte da URL como um parâmetro (do tipo double) e o fornece para a próxima rota direct:celsius-to-fahrenheit
.
rest("/convert")
.get("/celsius/to/fahrenheit/{num}")
.consumes("text/plain").produces("text/plain")
.description("Convert a temperature in Celsius to Fahrenheit")
.param().name("num").type(RestParamType.path).description("Temperature in Celsius").dataType("int").endParam()
.to("direct:celsius-to-fahrenheit");
- Finalmente, escrever a rota SOAP upstream e a transformação entre upstream e downstream.
from("direct:celsius-to-fahrenheit")
.removeHeaders("CamelHttp*")
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
// Inicializar solicitação SOAP
// Preencher o parâmetro downstream num no corpo, que é um tipo double simples.
CelsiusToFahrenheitRequest c = new CelsiusToFahrenheitRequest();
c.setTemperatureInCelsius(Double.valueOf(exchange.getIn().getHeader("num").toString()));
exchange.getIn().setBody(c);
}
})
// Especificar operação SOAP e namespace
// Definido no arquivo application.properties
.setHeader(CxfConstants.OPERATION_NAME, constant("{{endpoint.operation.celsius.to.fahrenheit}}"))
.setHeader(CxfConstants.OPERATION_NAMESPACE, constant("{{endpoint.namespace}}"))
// Enviar o pacote usando o bean do cliente SOAP gerado pelo WSDL.
.to("cxf:bean:cxfConvertTemp")
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
// Lidar com a resposta SOAP
// Preencher o corpo, que é um valor do tipo double, na string
// Retornar a string para o downstream
MessageContentsList response = (MessageContentsList) exchange.getIn().getBody();
CelsiusToFahrenheitResponse r = (CelsiusToFahrenheitResponse) response.get(0);
exchange.getIn().setBody("Temp in Farenheit: " + r.getTemperatureInFahrenheit());
}
})
.to("mock:output");
- Testar
curl localhost:9090/convert/celsius/to/fahrenheit/50
Temp in Farenheit: 122.0
Como podemos ver, usar o Camel para SOAP-to-REST requer definir rotas e lógica de conversão para todas as operações usando código Java, o que incorre em custos de desenvolvimento.
Da mesma forma, se o WSDL contiver muitos serviços e operações, usar o Camel para fazer proxy também pode ser doloroso.
2.3 Conclusão
Vamos resumir as desvantagens dos métodos tradicionais.
Módulo | Camel | |
---|---|---|
WSDL | análise manual | geração de código via Maven |
upstream | análise manual | conversão automática |
definir corpo | fornecer modelos para julgamento e conversão | escrever código de conversão |
obter parâmetros | variáveis Nginx | definir em código ou chamar interface do cliente SOAP para obter |
Ambas as abordagens têm custos de desenvolvimento, e para cada novo web service, esse custo de desenvolvimento precisa ser repetido.
O custo de desenvolvimento é proporcional à complexidade do web service.
3. Proxy SOAP-to-REST do APISIX
As abordagens tradicionais de proxy exigem fornecer modelos de conversão ou escrever código de conversão, ambas exigindo que os usuários analisem profundamente arquivos WSDL e incorram em custos significativos de desenvolvimento.
O APISIX fornece uma abordagem automatizada que analisa automaticamente arquivos WSDL e fornece lógica de conversão para cada operação, eliminando custos de desenvolvimento para os usuários.
Este é um plugin Enterprise, entre em contato conosco para mais informações.
3.1 Conversão Automática Sem Código
Usando o proxy SOAP do APISIX:
- Nenhuma análise ou importação manual de arquivos WSDL é necessária
- Nenhuma necessidade de definir modelos de conversão
- Nenhuma necessidade de escrever qualquer código de conversão ou acoplamento.
Os usuários só precisam configurar a URL do WSDL, e o APISIX realizará a conversão automaticamente. É adequado para qualquer web service, é um programa genérico e não requer desenvolvimento secundário para necessidades específicas.
3.2 Configuração Dinâmica
- A URL do WSDL pode ser vinculada a qualquer rota e, como outros objetos de recurso do APISIX, pode ser atualizada em tempo de execução. As mudanças de configuração entram em vigor dinamicamente sem a necessidade de reiniciar o APISIX.
- A(s) URL(s) do serviço contida(s) no arquivo WSDL (que pode ter várias URLs), ou seja, o endereço upstream, será automaticamente reconhecida e usada como o upstream SOAP sem a necessidade de os usuários analisarem e configurarem.
3.3 Mecanismo de Implementação
- Obter o conteúdo do arquivo WSDL a partir da URL do WSDL e gerar automaticamente um objeto proxy após análise.
- O objeto proxy é responsável pela conversão de protocolo:
- Gerar solicitações XML SOAP compatíveis com base na entrada JSON.
- Converter respostas XML SOAP para respostas JSON.
- Acessar o web service e lidar automaticamente com detalhes do protocolo SOAP, como respostas do tipo fault.
- Suportar SOAP1.1 e SOAP1.2 e vários recursos de extensão, como WS-Addressing.
3.4 Exemplo de Configuração
Explicação dos parâmetros de configuração do plugin SOAP:
Parâmetro | Obrigatório? | Descrição |
---|---|---|
wsdl_url | Sim | URL do WSDL, por exemplo, https://apps.learnwebservices.com/services/tempconverter?wsdl |
Teste:
# Configurar roteamento do APISIX usando o plugin SOAP
# Observe que uma única rota pode executar todas as operações, com o nome da operação especificado usando parâmetros de URL
# Isso também reflete os benefícios do proxy dinâmico, eliminando a necessidade de análise manual de cada operação no arquivo WSDL.
curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: xxx' -X PUT -d '
{
"methods": ["POST"],
"uri": "/getCountry",
"plugins": {
"soap": {
"wsdl_url": "http://localhost:8080/ws/countries.wsdl"
}
}
}'
curl 'http://127.0.0.1:9080/getCountry' \
-X POST -d '{"name": "Spain"}'
# Chamada bem-sucedida
HTTP/1.1 200 OK
Date: Tue, 06 Dec 2022 08:07:48 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.99.0
{"currency":"EUR","population":46704314,"capital":"Madrid","name":"Spain"}
# Chamada falhou
HTTP/1.1 502 Bad Gateway
Date: Tue, 03 Jan 2023 13:43:33 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.99.0
{"message":"Your name is required.","actor":null,"code":"SOAP-ENV:Server","subcodes":null,"detail":null}
4. Conclusão
No mundo de hoje, os web services avançaram ao ponto em que muitos usuários empresariais dependem de web services tradicionais baseados em SOAP para fornecer seus serviços. Devido a razões históricas e considerações de custo, esses serviços nem sempre são adequados para uma refatoração completa em serviços RESTful. Como resultado, há uma demanda significativa por SOAP-to-REST entre muitos usuários empresariais.
O plugin SOAP-to-REST fornecido pelo APISIX pode alcançar funcionalidade de proxy sem código, pode ser configurado dinamicamente e não requer desenvolvimento secundário, o que é benéfico para a migração e integração de negócios de custo zero para usuários empresariais.