Web Service SOAP legacy migrando pra REST
Como modernizar Web Services SOAP do Protheus legado pra REST com TLPP. Manter compatibilidade durante migracao.
SOAP nao morreu mas perdeu mercado pra REST/JSON. Customizacoes Protheus 2010-2018 tem muito WSDL/SOAP. Migracao pra TLPP REST e direta — mas precisa manter SOAP funcionando durante a transicao.
Antes — SOAP classico
// AdvPL legado - WSDL definition
WSSERVICE U_WSConsultaCli DESCRIPTION "Consulta cliente"
WSDATA cCodigo AS STRING
WSDATA cLoja AS STRING
WSDATA oRetorno AS STRUCT_CLI
WSMETHOD ConsultaCli DESCRIPTION "Busca cliente por codigo"
ENDWSSERVICE
WSSTRUCT STRUCT_CLI
WSDATA cNome AS STRING
WSDATA cEmail AS STRING
WSDATA nLimite AS FLOAT
ENDWSSTRUCT
WSMETHOD ConsultaCli WSRECEIVE cCodigo, cLoja WSSEND oRetorno WSSERVICE U_WSConsultaCli
SA1->(DBSetOrder(1))
If SA1->(DBSeek(xFilial("SA1") + cCodigo + cLoja))
oRetorno := WSClassNew("STRUCT_CLI")
oRetorno:cNome := AllTrim(SA1->A1_NOME)
oRetorno:cEmail := AllTrim(SA1->A1_EMAIL)
oRetorno:nLimite := SA1->A1_LC
EndIf
Return .T.
Depois — REST com TLPP
#include "tlpp-core.th"
namespace custom.api.clientes
@Get("/api/v1/clientes/:codigo/:loja")
function ConsultaCli(:codigo, :loja)
Local oRet := JsonObject():New()
SA1->(DBSetOrder(1))
If SA1->(DBSeek(xFilial("SA1") + :codigo + :loja))
oRet["nome"] := AllTrim(SA1->A1_NOME)
oRet["email"] := AllTrim(SA1->A1_EMAIL)
oRet["limite"] := SA1->A1_LC
oResponse:setStatus(200)
Else
oRet["error"] := "Cliente nao encontrado"
oResponse:setStatus(404)
EndIf
oResponse:setContentType("application/json")
Return oRet:ToJson()
Estrategia de migracao
- Inventario: liste todos WSDLs existentes. Use
SELECT * FROM SX2 WHERE X2_NOME = 'WS'ou grep nos fontes. - Cliente externo: identifique quem chama cada WS — alguns podem ja estar inativos.
- Manter SOAP em paralelo: nao desligue ate todos clientes migrarem.
- Migrar gradual: 1 WS por vez. Teste exaustivamente.
- Deprecation period: anuncie 3-6 meses antes de desligar SOAP.
Mapping SOAP → REST
| SOAP | REST |
|---|---|
| WSMETHOD GetCliente | @Get("/clientes/:id") |
| WSMETHOD CreateCliente | @Post("/clientes") |
| WSMETHOD UpdateCliente | @Put("/clientes/:id") |
| WSMETHOD DeleteCliente | @Delete("/clientes/:id") |
| WSDATA in struct | :param em path ou JSON body |
| WSDATA out struct | JsonObject() retornado |
| SOAP Fault | HTTP status code (4xx/5xx) + JSON error |
Versionamento da API
// Use prefixo de versao na URL
@Get("/api/v1/clientes/:id") // versao antiga, mantida
@Get("/api/v2/clientes/:id") // versao nova, com campos diferentes
// Em algum momento deprecar v1 e remover
Autenticacao
SOAP usa header WS-Security. REST tipicamente Bearer token (OAuth2/JWT).
@Get("/api/v1/clientes")
function ListaClientes()
Local cToken := oRequest:getHeader("Authorization")
If !ValidaToken(cToken)
oResponse:setStatus(401)
oResponse:setBody('{"error":"unauthorized"}')
Return
EndIf
// ... processar
return
Convivencia SOAP + REST
┌──────────────────────────────────────┐
│ Cliente legado (SOAP) │ ─→ Endpoint SOAP antigo (mantido)
└──────────────────────────────────────┘
┌──────────────────────────────────────┐
│ Cliente novo (REST) │ ─→ Endpoint REST novo
└──────────────────────────────────────┘
Backend compartilhado: ambos chamam mesma funcao Core (U_BuscaCli)
Vantagens do REST sobre SOAP
- Sem boilerplate XML — JSON compacto, ~10x menor
- HTTP semantico (GET, POST, PUT, DELETE) — intuitivo
- Status codes claros (200, 404, 500)
- Suporte nativo em qualquer linguagem moderna
- Postman, Insomnia, curl pra testar
- Documentacao via OpenAPI/Swagger
Pegadinhas
- SOAP tem schema rigoroso: REST e mais permissivo. Clients podem mandar JSON malformado — valide.
- Idempotencia: SOAP nao se preocupa; REST exige (PUT deve ser idempotente, GET nao deve ter side effect).
- Pagination: SOAP geralmente retorna tudo; REST deve paginar (?limit, ?offset).
- Documentacao: WSDL e auto-descritivo. REST precisa OpenAPI explicito.
- HTTPs obrigatorio: REST sem TLS expoe tokens.