Sistemas Distribuídos - Lab Prático SOAP Web Service

8 minute read

Published:

Neste laboratório, você irá implementar um serviço web SOAP em Python para simular a consulta de dados de alunos em um sistema acadêmico.

Ao final da atividade, você deverá ser capaz de:

  • Implementar um serviço SOAP simples em Python.
  • Disponibilizar operações por meio de um serviço web.
  • Acessar o documento WSDL gerado pelo serviço.
  • Criar um cliente Python para consumir o serviço SOAP.
  • Compreender, na prática, a relação entre serviço, contrato, requisição e resposta.

Neste laboratório, usaremos a biblioteca Spyne para criar o servidor SOAP e a biblioteca Zeep para consumir o serviço como cliente. O Spyne permite expor serviços com APIs bem definidas, enquanto o Zeep lê o WSDL e gera uma interface Python para consumir serviços SOAP.

Cenário do laboratório

Imagine que uma universidade possui um sistema acadêmico interno com dados de alunos. Outros sistemas, como o portal do aluno ou o sistema da secretaria, precisam consultar essas informações.

Em vez de acessar diretamente o banco de dados, esses sistemas irão consumir um serviço SOAP com as seguintes operações:

OperaçãoDescrição
consultar_alunoConsulta o nome e o curso de um aluno a partir do ID
verificar_matricula_ativaVerifica se a matrícula do aluno está ativa
listar_alunosRetorna uma lista simples com os alunos cadastrados

Estrutura esperada do projeto

Crie uma pasta chamada laboratorio-soap-python.

Dentro dela, crie os seguintes arquivos:

laboratorio-soap-python/
│
├── servidor.py
├── cliente.py
└── requirements.txt

Passo 1 — Criar o ambiente virtual

Abra o terminal na pasta do projeto e execute:

python -m venv venv

Ative o ambiente virtual.

No Windows:

venv\Scripts\activate

No macOS ou Linux:

source venv/bin/activate

Passo 2 — Criar o arquivo requirements.txt

Crie o arquivo requirements.txt com o seguinte conteúdo:

spyne
zeep
lxml

Instale as dependências:

pip install -r requirements.txt

A biblioteca Zeep é um cliente SOAP em Python que inspeciona o documento WSDL e cria uma interface programática para o serviço. A documentação também indica que sua instalação pode ser feita com pip install zeep. ([Zeep Documentation][2])

Passo 3 — Implementar o servidor SOAP

Crie o arquivo servidor.py com o código abaixo:

from spyne import Application, ServiceBase, rpc
from spyne import Integer, Unicode, Boolean, Iterable
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication

from wsgiref.simple_server import make_server


class ServicoAcademico(ServiceBase):
    """
    Serviço SOAP que simula operações básicas de um sistema acadêmico.
    """

    alunos = {
        101: {
            "nome": "Ana Souza",
            "curso": "Sistemas de Informação",
            "matricula_ativa": True
        },
        102: {
            "nome": "Bruno Lima",
            "curso": "Ciência da Computação",
            "matricula_ativa": True
        },
        103: {
            "nome": "Carla Mendes",
            "curso": "Engenharia de Software",
            "matricula_ativa": False
        }
    }

    @rpc(Integer, _returns=Unicode)
    def consultar_aluno(ctx, id_aluno):
        """
        Consulta os dados de um aluno pelo ID.

        Parâmetro:
            id_aluno: código numérico do aluno.

        Retorno:
            Texto com nome e curso do aluno.
        """

        aluno = ServicoAcademico.alunos.get(id_aluno)

        if aluno is None:
            return f"Aluno com ID {id_aluno} não encontrado."

        return f"{aluno['nome']} - {aluno['curso']}"

    @rpc(Integer, _returns=Boolean)
    def verificar_matricula_ativa(ctx, id_aluno):
        """
        Verifica se a matrícula do aluno está ativa.

        Parâmetro:
            id_aluno: código numérico do aluno.

        Retorno:
            True se a matrícula estiver ativa; False caso contrário.
        """

        aluno = ServicoAcademico.alunos.get(id_aluno)

        if aluno is None:
            return False

        return aluno["matricula_ativa"]

    @rpc(_returns=Iterable(Unicode))
    def listar_alunos(ctx):
        """
        Lista todos os alunos cadastrados.

        Retorno:
            Lista de textos contendo ID, nome e curso.
        """

        for id_aluno, dados in ServicoAcademico.alunos.items():
            yield f"{id_aluno} - {dados['nome']} - {dados['curso']}"


aplicacao = Application(
    [ServicoAcademico],
    tns="http://exemplo.com/servico-academico",
    in_protocol=Soap11(validator="lxml"),
    out_protocol=Soap11()
)

wsgi_app = WsgiApplication(aplicacao)


if __name__ == "__main__":
    servidor = make_server("127.0.0.1", 8000, wsgi_app)

    print("Servidor SOAP em execução.")
    print("Acesse o WSDL em: http://127.0.0.1:8000/?wsdl")
    print("Pressione CTRL+C para encerrar.")

    servidor.serve_forever()

Entendendo o código do servidor

Observe os principais elementos do código:

Parte do códigoFunção
ServiceBaseClasse base para definir um serviço
@rpcDecorador usado para expor métodos como operações remotas
Integer, Unicode, BooleanTipos de dados usados no contrato SOAP
ApplicationDefine a aplicação SOAP
Soap11Define o uso do protocolo SOAP 1.1
WsgiApplicationPermite executar o serviço como aplicação WSGI
make_serverCria um servidor HTTP simples para testes locais

O uso de Application, rpc, ServiceBase, Soap11 e WsgiApplication é compatível com a estrutura apresentada na documentação e exemplos do Spyne para criação de serviços SOAP em Python.

Passo 4 — Executar o servidor

No terminal, com o ambiente virtual ativado, execute:

python servidor.py

Se tudo estiver correto, você verá uma mensagem semelhante a:

Servidor SOAP em execução.
Acesse o WSDL em: http://127.0.0.1:8000/?wsdl
Pressione CTRL+C para encerrar.

Passo 5 — Acessar o WSDL

Abra o navegador e acesse:

http://127.0.0.1:8000/?wsdl

Você deverá visualizar um documento XML descrevendo o serviço.

Esse documento é o WSDL. Ele funciona como o contrato do serviço, informando quais operações estão disponíveis, quais parâmetros são esperados e quais tipos de dados serão retornados.

Questão para reflexão

Após abrir o WSDL no navegador, responda:

  1. O WSDL é escrito em qual formato?
  2. Quais operações aparecem no WSDL?
  3. O que o WSDL permite que um cliente saiba antes de consumir o serviço?

Passo 6 — Implementar o cliente SOAP

Agora crie o arquivo cliente.py com o código abaixo:

from zeep import Client


URL_WSDL = "http://127.0.0.1:8000/?wsdl"


def main():
    cliente = Client(URL_WSDL)

    print("Cliente SOAP criado com sucesso.")
    print()

    print("1. Consultando aluno 101")
    resultado = cliente.service.consultar_aluno(101)
    print("Resultado:", resultado)
    print()

    print("2. Verificando matrícula do aluno 101")
    matricula_ativa = cliente.service.verificar_matricula_ativa(101)
    print("Matrícula ativa?", matricula_ativa)
    print()

    print("3. Verificando matrícula do aluno 103")
    matricula_ativa = cliente.service.verificar_matricula_ativa(103)
    print("Matrícula ativa?", matricula_ativa)
    print()

    print("4. Listando alunos cadastrados")
    alunos = cliente.service.listar_alunos()

    for aluno in alunos:
        print(aluno)
    print()

    print("5. Consultando aluno inexistente")
    resultado = cliente.service.consultar_aluno(999)
    print("Resultado:", resultado)


if __name__ == "__main__":
    main()

Passo 7 — Executar o cliente

Mantenha o servidor em execução em um terminal.

Abra outro terminal na mesma pasta do projeto, ative o ambiente virtual e execute:

python cliente.py

Saída esperada:

Cliente SOAP criado com sucesso.

1. Consultando aluno 101
Resultado: Ana Souza - Sistemas de Informação

2. Verificando matrícula do aluno 101
Matrícula ativa? True

3. Verificando matrícula do aluno 103
Matrícula ativa? False

4. Listando alunos cadastrados
101 - Ana Souza - Sistemas de Informação
102 - Bruno Lima - Ciência da Computação
103 - Carla Mendes - Engenharia de Software

5. Consultando aluno inexistente
Resultado: Aluno com ID 999 não encontrado.

Entendendo o código do cliente

No cliente, a linha abaixo cria um consumidor SOAP a partir do WSDL:

cliente = Client(URL_WSDL)

A partir disso, é possível chamar as operações como se fossem métodos Python:

cliente.service.consultar_aluno(101)

O Zeep recupera o WSDL informado e usa esse contrato para permitir chamadas ao serviço SOAP.

Atividade prática

Agora modifique o serviço para incluir uma nova operação chamada:

consultar_curso

Essa operação deve:

  • Receber o ID de um aluno.
  • Retornar apenas o curso do aluno.
  • Retornar a mensagem "Aluno não encontrado." caso o ID não exista.

Orientação para implementação

No arquivo servidor.py, adicione o seguinte método dentro da classe ServicoAcademico:

@rpc(Integer, _returns=Unicode)
def consultar_curso(ctx, id_aluno):
    aluno = ServicoAcademico.alunos.get(id_aluno)

    if aluno is None:
        return "Aluno não encontrado."

    return aluno["curso"]

Depois, reinicie o servidor:

python servidor.py

No arquivo cliente.py, adicione uma chamada para testar a nova operação:

print("6. Consultando curso do aluno 102")
curso = cliente.service.consultar_curso(102)
print("Curso:", curso)

Resultado esperado da nova operação

Ao executar o cliente, você deverá obter algo semelhante a:

6. Consultando curso do aluno 102
Curso: Ciência da Computação

Teste também com um aluno inexistente:

curso = cliente.service.consultar_curso(999)
print("Curso:", curso)

Resultado esperado:

Curso: Aluno não encontrado.

Desafio extra

Implemente uma nova operação chamada:

calcular_media

A operação deve:

  • Receber três notas do aluno.
  • Calcular a média aritmética.
  • Retornar a média como texto.
  • Se alguma nota for menor que 0 ou maior que 10, retornar a mensagem "Nota inválida.".

Como o foco deste laboratório é SOAP e não validação avançada, utilize tipos simples.

Sugestão de assinatura:

@rpc(Float, Float, Float, _returns=Unicode)
def calcular_media(ctx, nota1, nota2, nota3):
    ...

Para isso, será necessário importar Float:

from spyne import Float

Exemplo de comportamento esperado

Chamada no cliente:

media = cliente.service.calcular_media(8.0, 7.5, 9.0)
print(media)

Saída esperada:

Média: 8.17

Chamada com nota inválida:

media = cliente.service.calcular_media(8.0, 11.0, 9.0)
print(media)

Saída esperada:

Nota inválida.