Pragiva — Tecnologia na prática

O objeto oRest: como a requisição entra e a resposta sai

oRest não é declarado: a annotation REST do TLPP o injeta. Os métodos reais, o homônimo perigoso e por que na classe é oRest:, nunca ::.

Protheus Referência técnica 5 min de leitura

Assista o vídeo

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, Public ou Data oRest, nenhuma atribuição oRest :=.
Dentro da classe
é sempre oRest:, nunca :: — não porque :: seja errado (ele é só um apelido de Self:), 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:

ADVPL / TLPP
@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:

ADVPL / TLPP
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):

ADVPL / TLPP
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

  1. TDNtdn.totvs.com

    TDN — oRest:updateKeyHeaderResponseA página retornou 403 na consulta direta.
  2. 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