Resposta rápida
- O que é
oResté o objeto que toda annotation REST do TLPP recebe pronto — não é uma classe que você declara nem instancia.- Como chega
- injetado pelo runtime da annotation; nenhuma declaração
Local,Static,PublicouData oRest, nenhuma atribuiçãooRest :=. - Dentro da classe
- é sempre
oRest:, nunca::— não porque::seja errado (ele é só um apelido deSelf:), mas porque o objeto não é da sua classe. - Homônimo
- existe um
oRest := FWRest():New(...)em alguns fontes, mas é o cliente HTTP que o Protheus usa para CONSUMIR uma API de fora — outra classe, outros métodos. - Onde mora a classe
- não é uma classe que se declara em código de aplicação — o comportamento aqui vem do que se observa nas chamadas, não de uma definição que se possa abrir e ler.
Consulta rápida10 seções
Um objeto que aparece sem ser declarado
Quem chega de outra linguagem de web procura, num controller REST em TLPP,
o lugar onde oRest foi criado. Não vai achar. Em qualquer controller que
chama oRest:setResponse(, não existe declaração Local, Static,
Public, Private ou Data oRest, e não existe oRest := alguma-coisa.
O identificador simplesmente existe dentro do corpo do método ou da função
anotada — o runtime da annotation injeta o objeto no escopo antes de rodar
o código do controller. Não é propriedade herdada: a maioria das
classes-controller anotadas não declara From nenhum. E não é Public de
módulo, que apareceria declarada em algum include. É injeção pura, e é por
isso que um oRest solto dentro de um método anotado não quebra a
compilação: ele já está lá quando o método começa a rodar.
O homônimo que engana
Um oRest aparece DECLARADO em alguns fontes, e é uma armadilha real:
oRest := FWRest():New(...). Esse FWRest é um objeto cliente HTTP — o
Protheus usando ele para consumir uma API de fora, não para responder a
uma. Os métodos são outros: SetPath, GetResult, GetLastError, Post,
Get, Put, Delete, GetHTTPCode. Nenhum deles existe no oRest
injetado pela annotation, e nenhum dos métodos deste artigo existe no
FWRest. Se um fonte chama oRest:Post(...) ou oRest:GetHTTPCode(), é
esse outro objeto — normalmente com FWRest():New( na mesma função, poucas
linhas antes da chamada que confunde.
oRest:, nunca ::
Este é o ponto que mais confunde quem migra do WSRESTFUL clássico para a
annotation. Numa classe que combina annotation de verbo e declaração
class ao mesmo tempo, a forma de chamar o objeto de resposta é sempre
oRest:<método>(; ::<método>( simplesmente não resolve contra nada útil
ali. E o contraponto fecha o raciocínio: ::SetResponse(...) existe, e é
comum — só que sempre em fontes que ainda têm WSRESTFUL, nunca em
fontes com annotation.
Antes de tirar a conclusão errada: o problema não é o ::. Ele é
apenas um apelido — a linguagem define #xtranslate :: => Self: como
sintaxe geral de orientação a objeto, usada em qualquer classe TLPP, com
ou sem REST envolvido. Escrever ::meuMetodo() numa classe-controller é
correto e continua sendo.
O que muda é para ONDE ele aponta. No WSRESTFUL, o serviço é o próprio
objeto que responde: ::SetResponse resolve contra a classe do serviço,
que declara esse método. No modelo anotado a classe é sua, e quem responde
é outro objeto, injetado de fora — ::setResponse resolveria contra o seu
controller, que não declara nada parecido. Por isso migrar um WSRESTFUL
trocando só o cabeçalho e mantendo os :: do corpo é o erro mais fácil de
cometer: o compilador aceita, porque :: sempre compila numa classe
válida, e a falha só aparece quando a primeira requisição chega.
Também existe fora de classe
Nem toda API usa classe: uma parcela relevante das rotas anotadas fica
direto acima de uma Function solta. oRest também está disponível ali,
sem diferença de comportamento, e pode inclusive circular como argumento
comum:
@Get("api/retail/v1/orcamento")
Function Orcamento()
Local orcamento := Orcamento():New( oRest, oRest:getQueryRequest())
orcamento:Get()
Return oRest:setResponse( EncodeUtf8(orcamento:getReturn()) )Ou seja: oRest não é recurso da classe-controller. É injetado pela
annotation em qualquer bloco anotado, função ou método.
Os métodos mais usados
setResponse é o método mais central de todos: fecha praticamente toda
chamada bem-sucedida, gravando o corpo que volta ao consumidor.
getQueryRequest e getPathParamsRequest leem o que chegou pela URL — o
primeiro pela query string, o segundo pelo path da rota. setStatusCode
grava o código HTTP. setKeyHeaderResponse grava cabeçalho de resposta.
getBodyRequest devolve o corpo bruto de um POST ou PUT. setFault sinaliza
erro. Os demais — getHeaderRequest, getFullURLRequest,
updateKeyHeaderResponse, setHeaderResponse, ExistKeyHeaderRequest —
aparecem com frequência bem menor, cada um resolvendo um caso específico.
A caixa varia livremente: setResponse e SetResponse convivem sem
problema — a mesma insensibilidade a caixa do resto da linguagem, aplicada
a este objeto também. Nenhuma das duas grafias é “a errada”.
Como se lê o que chegou
Quatro métodos respondem por quase toda leitura da requisição.
getQueryRequest() devolve a query string já estruturada como objeto
JSON, sem exigir parser escrito à mão. getPathParamsRequest() devolve um
dicionário indexado pelo nome declarado na própria rota da annotation —
sem os dois-pontos: numa rota escrita como
/api/stock/trackcosts/kardex/v1/D4B/:id, a leitura é
oRest:getPathParamsRequest()['id']. getBodyRequest() devolve o corpo
bruto da requisição para quem prefere decodificar o JSON por conta
própria. E getHeaderRequest() lê cabeçalho da própria requisição — não
confundir com setKeyHeaderResponse, que grava cabeçalho na resposta, não
na leitura.
setFault não tem onde receber um código
setFault recebe um único parâmetro, sempre — o corpo do erro já pronto,
em string. Não existe uma forma com dois argumentos, nem uma variante que
aceite um código numérico:
oRest:setFault( jResult['result']:ToJson() )
oRest:setFault('{"error": "' + cMsg + '"}')Isso explica por que uma resposta que passa por setFault costuma sair
como erro de servidor: o método não tem parâmetro para carregar outro
código. Do lado de quem consome, isso apaga uma distinção que importa —
erro de negócio (“cliente não existe”) e falha técnica (variável
inexistente, tabela travada) chegam iguais, e quem recebe não tem como
saber, só pelo código, se errou o pedido ou se o servidor quebrou. Quem
precisa de um código próprio chama setStatusCode numa linha e
setResponse na seguinte, e é por isso que esse par virou o caminho
padrão.
setStatusCode aceita qualquer número
setStatusCode também recebe um único parâmetro, numérico. O framework
não valida o valor — aceita o que for passado e devolve exatamente aquilo
no cabeçalho da resposta. Um código HTTP inválido, fora da tabela oficial,
compila e roda normalmente, sem aviso nenhum.
O cabeçalho de resposta tem um nome só
O método real para gravar cabeçalho de resposta é
setKeyHeaderResponse(chave, valor):
oRest:SetKeyHeaderResponse("Content-Type", "application/json")
oRest:SetKeyHeaderResponse("Content-Disposition", "attachment; filename=arquivo.pdf")setHeaderResponse existe, mas é bem mais raro. SetResponseHeader — o
nome que soa mais natural para quem vem de outra API REST — não existe:
não compila em nenhuma caixa.
O que fecha a referência
A definição de oRest não é algo que se abra em código de aplicação para
ler. Tudo neste artigo vem do comportamento observado nos controllers, não
de uma declaração Class oRest disponível para inspeção. A distinção não é
preciosismo: comportamento observado prova uso, não contrato.
Precisa mapear como o oRest se encaixa no resto de uma API REST em TLPP? fale com a gente.
Referências
TDNtdn.totvs.com
TDN — oRest:updateKeyHeaderResponseA página retornou 403 na consulta direta.Outrogithub.com
GitHub — totvs/tlpp-sample-restRepositório público de exemplos oficiais de REST em TLPP com o objeto oRest, consultado como leitura complementar.
Continue por aqui
- Vídeo
ProtheusGuia de processo
API REST em TLPP: o que muda em relação ao AdvPL
API REST em TLPP se escreve com annotation, e o WSRESTFUL do AdvPL ainda expõe boa parte do padrão: as duas gerações convivem. O que muda entre elas.