Resposta rápida
- O que é
- a annotation (
@Get,@Post…) que o compilador TLPP lê acima de um método ou função e transforma em rota de API REST, semWSRESTFULnemWSMETHOD. - Onde aparece
- sobre a declaração de um método, dentro de uma
class, ou direto acima de umaFunctionsolta — classe é a forma mais comum. - A caixa importa?
- Não —
@Get,@GETe@getfuncionam do mesmo jeito; não é convenção de estilo, é insensibilidade a caixa da própria linguagem. - Path param
- dois-pontos dentro da própria string da rota (
:id); a forma{id}do OpenAPI não é reconhecida pelo compilador. - Função ou classe?
- As duas funcionam; classe é o padrão que a TOTVS segue com mais frequência.
Consulta rápida7 seções
Antes de decidir onde e como escrever a próxima annotation, vale saber como a própria TOTVS escreve as suas — não como opinião de curso, mas como observação de um vocabulário que se repete de fonte para fonte.
O REST em AdvPL — WSRESTFUL, WSMETHOD, WSDATA — não foi removido nem
substituído de uma vez: continua presente em boa parte do padrão. Mas é a
annotation, em TLPP, que a TOTVS usa para escrever API nova. As duas
gerações convivem lado a lado; a annotation é a que está crescendo.
Cinco verbos, dois que não existem
@Get, @Post, @Put, @Delete e @Patch são os únicos verbos
reconhecidos pelo compilador. Get é disparado o mais comum; Patch é
raro, o menos usado de todos. @Head e @Options não existem como
annotation — quem estiver migrando um contrato OpenAPI completo, com
HEAD para checar existência, não encontra equivalente pronto na
linguagem.
A caixa não importa — e isso é observável, não folclore
Este é o fato mais limpo da linguagem: a mesma annotation aparece escrita
em três grafias diferentes ao longo do padrão — @Get, @GET e @get —
e o compilador aceita as três sem reclamar, para qualquer um dos cinco
verbos. “A caixa não importa” deixou de ser instrução de instrutor: é
observação recorrente de gente diferente escrevendo o mesmo verbo de
jeitos diferentes, sem que nenhuma das formas quebre.
Três jeitos de escrever a mesma rota
A string da rota não precisa vir sozinha entre parênteses. Três formas
convivem no vocabulário real da linguagem: a posicional, mais comum de
longe (@Get("rota")); a nomeada, com parâmetros explícitos; e a que usa
uma constante #define no lugar do literal.
@Get(endpoint='/api/rh/v1/feedz/:id', description='descrição do endpoint')Só existem dois parâmetros nomeados reconhecidos: endpoint= e
description=. E description= nunca aparece sozinho — sempre acompanha
um endpoint= na mesma chamada. Vale dizer também o que não existe,
porque são exatamente as invenções que circulam por aí: params=,
tenant, wsservice, produces e consumes não são parâmetros
reconhecidos — junto com path, route, security e as próprias
annotations @Restful, @Api e @Route. Nenhuma dessas formas compila.
O parâmetro de rota é a própria string
Path param não tem cláusula própria — é sintaxe dentro do endpoint,
dois-pontos mais o nome, e é presença comum em rota que identifica um
registro específico. A forma {id}, comum em OpenAPI e em frameworks como
Spring, não é reconhecida: não é que seja desaconselhada, é que não
compila como o TLPP espera. Também não existe um parâmetro params= para
declarar path param à parte — a única declaração possível é a própria
string da rota; quem escreve o endpoint já escreveu, ali mesmo, o contrato
de entrada dele.
@Get("/api/sigaloja/smartview/v1/options/:modulo/:objeto/:funcao/:opcao")Quatro parâmetros encadeados na mesma rota, cada um lido depois por
oRest:getPathParamsRequest()['modulo'], ['objeto'], e assim por diante —
a chave é o nome declarado, sem os dois-pontos. E a mesma rota pode
aparecer em mais de uma annotation, uma por verbo:
@Get("api/stock/trackcosts/kardex/v1/D4B/:id")
@Put("api/stock/trackcosts/kardex/v1/D4B/:id")
@Delete("api/stock/trackcosts/kardex/v1/D4B/:id")Função solta ou método de classe
A annotation funciona nos dois lugares, e a preferência real não é igual:
classe, dentro de class... EndClass, é o formato mais frequente —
o contrário do que boa parte do material de curso ensina. Há um detalhe
que separa uma annotation que funciona de uma que o compilador ignora: ela
precisa ficar sobre a declaração do método, dentro do bloco class,
nunca sobre a implementação:
Class ProductTypeController
public method new()
@Get("api/stock/process/v1/producttypes")
public method getProductType()
EndClass
Method getProductType() Class ProductTypeController
...
ReturnA annotation nunca funciona sobre o corpo do método
(Method getProductType() Class ProductTypeController) — só sobre a linha
declarada dentro do Class... EndClass. Escrevê-la no lugar errado não
gera erro de compilação; simplesmente não vira rota, e o sintoma só
aparece em tempo de execução, na hora de subir o serviço.
Sem classe, a annotation fica direto acima da Function, e o objeto de
resposta continua disponível — prova de que ele não depende de a rotina
ser método de classe:
@Get("api/retail/v1/orcamento")
Function Orcamento()
Local orcamento := Orcamento():New( oRest, oRest:getQueryRequest())
orcamento:Get()
Return oRest:setResponse( EncodeUtf8(orcamento:getReturn()) )Repare que o objeto de resposta é passado como parâmetro de um construtor
comum, Orcamento():New(oRest,...) — a rotina anotada não precisa ser a
única a manuseá-lo.
O construtor que toda classe-API declara
Toda classe que carrega annotation de verbo declara public method new(.
Vale separar o que é observação do que é dedução: que o compilador
exige esse construtor para a classe funcionar como controller é
inferência a partir do desenho — nada comprova isso por si só, porque
nenhum caso real tenta subir sem ele. Na prática, isso significa que uma
classe-controller que só declara os métodos anotados e nunca o New()
compila sem erro nenhum — e falha exatamente no momento em que o serviço
sobe, quando o runtime tenta instanciar o controller e não acha
construtor. É um erro que aparece tarde, longe da linha que causou.
O fecho raro: @annotation... @end
Existe uma segunda gramática de annotation no TLPP, para declarar campo customizado — não verbo de rota. É rara, um recurso de nicho, mais visto em módulos de folha de pagamento e integrações de arquivo estruturado:
@annotation MagneticFileField
size as numeric default 0
position as numeric
fillment as character default " "
@endParâmetro sem default (position, no exemplo) fica obrigatório no uso —
de novo, é leitura da forma, não contagem. E vale fechar com o ponto que
explica por que @Get, @Post, @Put, @Delete e @Patch não aparecem
nessa gramática: eles não são declarados por quem escreve a API. Vêm
prontos do runtime do compilador — a annotation de verbo é reconhecida
por nome, não montada por quem escreve a API.
Se uma annotation compilou e o endpoint não responde, fale com a gente.
Referências
TDNtdn.totvs.com
TDN — Criando o primeiro serviço (Hello World) com REST em TLPPA página retornou 403 na consulta direta, sem login.TDNtdn.totvs.com
TDN — Migração de WSRESTFUL para REST em TLPPMesma ressalva: consulta direta bloqueada por 403.
Continue por aqui
- Vídeo
ProtheusTutorial
API GET: a listagem e o detalhe de um registro
GET de listagem devolve um resumo por SQL; GET de detalhe busca o registro inteiro por DbSeek pela chave do path. A armadilha do DbSeek de largura fixa.