Manipulação dos dados

O REST Framework utiliza as definições de modelo de dados e regras de negócios declaradas por meio de arquivos de definições de modelo de dados (x-model). Por meio desses arquivos, a API do REST Framework possui acesso a todas as regras de negócio definidas para as classes de dados, como regras de preenchimento de campos, validações gerais e regras definidas em eventos do sistema, que serão executados por ele durante o consumo de um recurso caso o controlador utilize a API de manipulação das entidades das classes de dados.

Esse modelo de definições via arquivos x-model é compartilhado pelo REST Framework e pelo Web Framework, garantindo que as regras associadas às classes de dados definidas em um único local sejam respeitadas em qualquer cenário de uso, seja em uma manipulação de dados realizada por um outro sistema via uma API HTTP, seja por um usuário do sistema alterando uma grade do Web Framework.

No atendimento de uma requisição HTTP, existem responsabilidades que são do REST Framework e outras que são dos controladores criados pelos desenvolvedores da plataforma. Abaixo detalhamos as responsabilidades de cada um deles.

REST Framework

  • Prover uma forma compreensiva de realizar a ligação entre operações de recursos e rotas.
  • Tratar a camada do protocolo HTTP, gerando respostas no formato adequado.
  • Controlar um conjunto de sessões JavaScript stateless que serão utilizadas para executar os métodos dos objetos controladores.
  • Prover um padrão de documentação da API, assim como a ferramenta para extração e visualização dessa documentação.
  • Possibilitar uma gestão de exceções e erros compatível com o modelo REST.
  • Possibilitar o rastreamento de erros após a execução pelo suporte por meio de um ticket de erro.
  • Prover facilidades para realizar validações das regras de negócio e segurança do sistema.
  • Prover facilidades para realizar transformações de objetos de negócio, fontes de dados, entidades do modelo de dados, chaves, DataSets e erros em JSON.

Controlador

  • Manter a integridade relacional entre os dados enviados pelo cliente e os já existentes no banco de dados.
  • Realizar todas as validações necessárias, incluindo segurança, para garantir a integridade dos dados da requisição, fazendo uso de APIs de alto nível disponibilizadas pelo REST Framework.
  • Realizar validação das regras de negócio.

Observar que o controlador é responsável por realizar validações de integridade, de regras de negócio e de segurança, mas essas lógicas não devem ser implementadas no controlador em si. Ele deve delegar esse papel a outras classes, como os objetos de gestão. A responsabilidade do controlador consiste em usar as APIs adequadas a fim de garantir que essas validações sejam realizadas.

Ações básicas

Os objetos controladores normalmente implementam um conjunto de ações básicas usadas em entidades de um banco de dados relacional. São elas:

  • Inserir
  • Obter
  • Atualizar
  • Remover

O protocolo HTTP define um conjunto de semânticas para os seus métodos, que necessitam ser respeitadas em uma API REST. Cada método do HTTP é classificado de duas formas: seguro e/ou idempotente. Um método é dito como seguro se ele não modifica um recurso. Idempotente é o método que tem o mesmo resultado independentemente do número de vezes que ele é chamado. Ao respeitar essas semânticas, a aplicação pode tirar proveito da infraestrutura já existente na Web, como o cache de servidores proxy. No entanto, por decisões de arquitetura, é comum vermos aplicações que não seguem fielmente o protocolo REST, podendo ser dito que elas são baseadas no REST. As regras definidas para o comportamento dos métodos HTTP são as seguintes:

Método Escopo Semântica Idempotente Seguro
GET Recurso Obtém um único recurso Sim Sim
GET Coleção Obtém todos os recursos Sim Sim
HEAD Recurso Obtém um único recurso (somente cabeçalho) Sim Sim
HEAD Coleção Obtém todos os recursos (somente cabeçalho) Sim Sim
POST Coleção Insere um novo recursos numa coleção Não Não
PUT Recurso Atualiza um recurso Sim Não
PATCH Recurso Atualiza um recurso Não Não
DELETE Recurso Apaga um recurso Sim Não
OPTIONS Qualquer Obtém as ações disponíveis para o recurso Sim Sim

A idempotência é importante na construção de uma API robusta. Se a aplicação cliente, ao atualizar um recurso, chamar o método POST várias vezes, resultará numa atualização incorreta? Como deve reagir uma aplicação cliente se, ao enviar uma requisição POST, ocorrer um timeout? É seguro para a aplicação enviar a requisição novamente, ou ela precisa antes verificar o status do recurso? Usando métodos idempotentes não é necessário responder essas questões, pois é padronizado que para esses métodos é seguro reenviar a requisição até receber uma resposta do servidor. Os métodos PUT e POST são ambos inseguros, sendo que PUT é idempotente, ao contrário de POST.

Outras ações que alteram um recurso

Nem toda as ações sobre um recurso podem ser representadas por meio das operações básicas acima descritas. Para representar essas ações específicas sobre um determinado recurso, pode-se criar uma rota específica, acrescentando um verbo no tempo infinitivo ao final da URL do recurso que sofrerá a ação. Por exemplo, em um recurso “Pedido” podemos ter a ação de aprovar:

{ method: 'POST', url: '/api/operacoes/v1/pedidos/:chcriacao/aprovar', action: 'aprovar(chcriacao)' }

Apesar dessa abordagem ser válida, dê preferência a tentar criar uma representação de estado para uma ação. No exemplo acima, a aprovação de um pedido pode ser modelada como o estado “aprovacao” que pode ser obtido para saber se um pedido está aprovado ou não, pode ser criado (aprovar) e excluído (desaprovar). Nessa abordagem, as rotas seriam:

{ method: 'GET', url: '/api/operacoes/v1/pedidos/:chcriacao/aprovacao', action: 'obterAprovacao(chcriacao)' },
{ method: 'POST', url: '/api/operacoes/v1/pedidos/:chcriacao/aprovacao', action: 'aprovar(chcriacao)' },
{ method: 'DELETE', url: '/api/operacoes/v1/pedidos/:chcriacao/aprovacao', action: 'desaprovar(chcriacao)' }

Modelando relações semânticas

Relações entre recursos podem ser representadas por intermédio de links ou sub-coleções. As seguintes regras devem ser usadas para se obter uma consistência na API.

Numa relação 1:N, onde o objeto alvo possui uma relação de dependência e não pode existir sem que exista o recurso principal, ela deve ser representada como uma sub-coleção. Por exemplo: uma operação de pedido ou provisão estabelece uma relação de um 1:N com os seus itens e esses somente podem existir se a operação existir:

{ method: 'GET', url: '/api/operacoes/v1/pedidos/:chcriacao/itens/:chave', action: 'obterItem(chcriacao, chave)' }

Numa relação 1:N, onde o dado é associado à ligação, ela deve ser representada como uma sub-coleção. Estamos nos referindo ao dado que não pertence nem ao recurso principal, nem ao recurso alvo, como as classes de vínculos. Por exemplo: os vínculos entre os produtos e suas imagens devem ser modelados como uma sub-coleção:

{ method: 'GET', url: '/api/cadastros/v1/recursos/:chave/imagens', action: 'listarImagens(chave)' }

Em qualquer outra relação 1:N, é recomendado o uso de links relacionado os recursos. O exemplo mais comum dessa relação são os campos lookup, onde a propriedade de um recurso guarda apenas uma identificação para outro.

No caso de uma relação N:M, é recomendado o uso de uma sub-coleção, definida na relação de busca mais comum. Se a procura ocorre frequentemente em ambas as relações, duas sub-coleções podem ser definidas. Por exemplo: a relação entre grupos e usuários é N:M. Ambas as rotas abaixo são adequadas para permitir o acesso dessas informações:

{ method: 'GET', url: '/api/seguranca/v1/grupos/:grupo/usuarios', action: 'listarUsuarios(grupo)' },
{ method: 'GET', url: '/api/seguranca/v1/usuarios/:usuario/grupos', action: 'listarGrupos(usuario)' }

Classes Entity e EntitySet

Juntamente com o REST Framework, foi disponibilizada uma API de acesso aos registros das classes do modelo de dados. Essas classes permitem que o desenvolvedor manipule um registro com todas as regras de negócio definidas nos arquivos x-class da classe, arquivos esses agora chamados de x-model. Também são validadas as permissões de modificação e visualização do usuário. O objetivo dessa nova API é prover ao desenvolvedor o mesmo nível de validação que ocorre nas grades do Web Framework. As principais classes dessa API são:

  • Entity: permite o acesso e a manipulação de um registro de uma classe de dados.
  • EntitySet: permite o acesso e a manipulação de um conjunto de registros de uma classe de dados, como os registros da classe “Usuários” ou o dataSet “pedido” da OperacaoPedido.

O método EntitySet.fromClass pode ser utilizado para criar um EntitySet quando os dados de uma classe estão no cache local do Engine. Exemplo:

MyUserController.prototype.list = function (request) {
  return this.ok(EntitySet.fromClass(ClassKeys.USERS));
};

Quando os dados não estão no cache local ou quando é necessário manipular dados que hoje são expostos como DataSets, caso de alguns objetos de gestão, pode-se utilizar o método EntitySet.fromDataSet. Exemplo:

function ControladorPedidoVendas() {
  this.cabecalhoPedido = Entity.fromDataSet(Classes.VENDAS, ds, { 
    fields: function (fld) {                                                             
      return fld.ehCabecalho && outraCondicaoComplexa(fld);                              
    }                                                                                     
  }
}

ControladorPedido.prototype.obterCabecalho = function (chcriacao) {                    
  this.operacao.abre(chcriacao);                                                       
  this.cabecalhoPedido.bindDataSet(this.operacao.pedidoCab);                           
  return this.cabecalhoPedido;                                                         
};

Quando são utilizadas as classes Entity e EntitySet são realizadas as validações de estrutura e de regras de negócio da classe de dados, contidas nos arquivos do tipo x-class e x-model. Elas são compartilhadas com o Web Framework, garantindo que ambas tecnologias realizem as mesmas validações. As verificações a nível de entidades são classificadas em dois tipos:

  • Estruturais:
    • As propriedades do JSON de entrada precisam obedecer o esquema das tabelas do banco de dados a partir das definições de modelo. Isso quer dizer que o REST Framework retornará um erro se for identificada uma propriedade no JSON que não está no modelo de dados.
    • A partir das entidades, o tipo de dado deve estar correto e as permissões do usuário devem ser validadas para alterar/inserir/consultar/excluir o registro/campo.
    • Os valores dos campos precisam atender as regras definidas nos campos.
  • Regras de negócio:
    • A entrada deve respeitar as regras especificadas no contexto de campos e de tabela durante a emissão dos listeners para os tipos de evento registrados na classe do dado.

É importante ressaltar que um controlador HTTP que não utilize essas classes tem a responsabilidade de realizar validações equivalentes a fim de garantir a integridade e segurança dos dados do sistema.

Validações estruturais dos dados

Nome

Propriedades envolvidas:

Verifica se as propriedades no JSON de entrada possuem paridade com a interface REST e o modelo de dados.

  1. O nível da declaração tem a ver com a forma de como o modelo do recurso REST foi declarado no controlador com base nas collections/entities. Exemplo:
Entrada REST
POST /api/exemplo/v1/requisicoes/
{
    "classe": -1899999777
    "recurso": 1234112,
    "quantidade": 1,
    "requisitan": 234543,
    "observacao": "Para consumo diário"
}

Definição da entity requisição no controlador:

const requisicoes = EntitySet.fromClass(-2008877000 /* Requisições */);
const requisicao = requisicoes.newEntity();
requisicao.assign(request.body.asJson());
requisicoes.persist();
return this.ok(requisicao);

Definição da classe Requisições (-2008877000):

let fld = this.field('RECURSO', 'int64');
fld.classKey = -2007800000 /* Recursos */;
fld.dataDictionary = 'Campo que contém o recurso que está sendo requisitado. Corresponde ao campo CHAVE da tabela RECURSO.';
fld.required = true;

fld = this.field('QUANTIDADE', 'number');
fld.dataDictionary = 'Campo que contém a Quantidade que está sendo requisitada.';
fld.required = true;

fld = this.field('REQUISITAN', 'int64');
fld.classKey = -2007890000 /* Pessoas */;
fld.dataDictionary = 'Campo que contém o Requisitante do recurso. Corresponde ao campo CHAVE da tabela ENTIDADE.';

fld = this.field('OBSERVACAO', 'memo');
fld.dataDictionary = 'Campo que contém qualquer observação sobre a requisição.';

As propriedades do JSON de entrada devem corresponder as definições do modelo dados da classe utilizada pelo EntitySet ou Entity.

  1. As propriedades do JSON deve ser os nomes dos campos normalizados em caixa baixa.

Tipo e Tamanho

Propriedades:

É verificado se o valor declarado no JSON é do mesmo tipo especificado na definição do campo no arquivo de modelo da forma descrita a seguir:

  • string e memo: o valor será limitado ao tamanho máximo. Caso exceda, será lançado um erro.
  • integer: o valor deve ser um número inteiro.
  • number: o valor deve ser um número inteiro ou um ponto flutuante.
  • date: o valor do campo deve ser nulo ou deve ser uma string no padrão ISO-8601.
  • boolean: são validados pela propriedade stringIfTrue.
    • Se for informado o valor true (boolean) o conteúdo será transformado para a string definida na propriedade stringIfTrue.
    • Se for informado como valor uma string, essa só poderá ser a que está definida na propriedade stringIfTrue. Se for diferente e não null (que é considerado como false), será lançado um erro.
    • Na resposta de uma requisição REST que realiza uma consulta a uma entidade do recurso, os campos desse tipo serão retornados como true ou false e não com o conteúdo da propriedade stringIfTrue.
  • combo: o valor deve ser nulo ou um dos valores contidos na propriedade options.
  • masterDetail: ao excluir um registro da classe, a propriedade masterDeleteAction determina o comportamento dos registros detalhes, de forma similar ao que ocorre ao excluir um registro de uma grade mestre. Os registros detalhes poderão ser excluídos, desvinculados ou o registro não poderá ser excluído enquanto houver detalhes. Nesta versão inicial da API, não é permitido informar os dados de uma detalhe de forma embarcada (campo detalhe preenchido com um array de outros objetos). Será retornado um erro caso o desenvolvedor tente consumir a API REST dessa forma. O consumidor do serviço deve realizar uma outra requisição para cadastrar os registros detalhes. Exemplo: uma entidade com endereços deve ser gravada em duas requisições, uma para a entidade em si e outra para os endereços. Esse comportamento deve ser alterado nas versões futuras para permitir a gravação em uma única requisição.
  • grid: é ignorado pelos serviços REST.
  • tree: é ignorado pelos serviços REST.
  • file: é um tipo de dados deprecated utilizado para indicar que um campo contém uma lista de chaves da VFS. Ele é tratado de forma similar a um lookup múltiplo.

Por uma questão de compatibilidade, todos os tipos que definem máscaras de validação são respeitados pela REST Framework.

Valor padrão

Propriedades:

Será assumido o valor da propriedade defaultValue caso não seja informado o valor de um campo na criação de uma entidade.

Preenchimento requerido

Propriedades:

Se required for true:

  • Não serão aceitos os valores null ou undefined.
  • Também não serão aceitos os valores '' (string vazia) quando o tipo for string ou memo.

Precisão decimal

Propriedades:

Valores numéricos serão ajustados para satisfazer a precisão decimal configurada.

Chaves estrangeiras

Propriedades:

Eventos:

Serão aceitos apenas valores nulos ou chaves da classe indicada por classKey, levando em consideração o tipo da relação definido por lookupType:

  • LookupType.RECORD: a chave informada deve ser de um registro cuja classe classKey ou uma de duas filhas.
  • LookupType.CLASS: a chave informada deve ser classKey ou de uma classe filha de classKey.
  • LookupType.FILE: a chave informada deve deve ser de um arquivo da VirtualFS, no diretório indicado por classKey ou em um sub-diretório dele.

Se a propriedade multiple for true, poderão ser informadas múltiplas chaves por meio de uma lista separada por “,” ou um array de chaves.

As chaves informadas também deverão satisfazer o evento de filtro lookupAddResult. Caso definidos, os eventos serão executados na seguinte ordem:

  1. beforeLookupAddResult
  2. lookupAddResult
  3. afterLookupAddResult

Formatação de textos

Propriedades:

Conteúdos textuais terão a sua caixa ajustada de acordo com a propriedade caseType e terão espaços no início e no final removidos caso a propriedade autoTrim seja true.

Valores máximo e mínimo

Propriedades:

Valores numéricos serão limitados dentro da faixa estabelecida pelas propriedades min e max.

Alteração de campos em registros de chave negativa

Propriedades:

Via de regra, campos de registros de chave negativa somente podem ser alterados na base de desenvolvimento do produto do qual o registro faz parte. Essa regra no entanto pode ser alterada por meio da propriedade userCanChangeNegativeKey. Quando ativa, o usuário com permissão de alteração na classe do registro poderá modificar o campo, por mais que o registro faça parte de um produto do sistema.

Campo de banco de dados

Propriedades:

Apenas campos que possuem o valor true nesta propriedade serão manipulados pelo REST Framework. Um erro será lançado caso se tente alterar um campo que tenha essa propriedade definida como false. A razão disso é que campos que tenham essa propriedade false não existem no esquema da tabela da base de dados e não podem ser persistidos.

Somente Leitura

Propriedades:

Será lançado um erro se readOnly for true e o valor do campo informado for diferente de nulo.

Ordem

Propriedades:

Utilizado apenas para organizar os campos em processos que necessitem exibir os campos do modelo de dados para o usuário, como o gerador de relatórios e ferramentas de geração de documentação. Também serve como sugestão para a configuração de visão, portanto a ordem deve ser a de relevância das informações, sendo os campos de menor ordem os de maior relevância.

A ordem de atribuição dos campos pelo REST Framework é indeterminada, portanto as regras de negócio não devem depender da ordem dos campos.

Validações de eventos de regras de negócio

Abaixo são relacionados os eventos que são disparados a nível de registro durante o fluxo de uma requisição REST que utilize a API de entidades para acesso a dados.

Inserção

Eventos emitidos durante uma inserção no banco de dados, utilizando a API de acesso aos dados como EntitySet.insert, EntitySet.newEntity e EntitySet.merge.

Eventos emitidos durante a efetivação da inserção da entidade:

Eventos emitidos caso a inserção seja cancelada pelo método Entity.cancel em vez de efetivada:

Edição

Eventos disparados durante uma edição da entidade, utilizando a API de acesso aos dados Entity.edit, EntitySet.update e EntitySet.merge.

Eventos emitidos durante a efetivação da edição da entidade:

Eventos emitidos caso a edição seja cancelada pelo método Entity.cancel em vez de efetivada:

Remoção

Eventos emitidos durante uma remoção no banco de dados, utilizando a API de acesso aos dados EntitySet.remove e Entity.delete.

Alteração de campo

Ao modificar um campo são emitidos os eventos:

Além desses, podem ser emitidos os eventos abaixo caso o campo alterado seja um lookup:

Compatibilização das classes de dados

Historicamente, as definições do modelo de dados eram realizadas apenas por arquivos do tipo x-class. Nesses arquivos, havia uma mistura de definições que diziam respeito ao modelo de dados e regras de negócios, e configurações que definiam como os dados deviam ser exibidos nas grades dos Web Framework.

Com a criação do REST Framework, essas definições de classes sofreram segregação de responsabilidades com o objetivo de separar as configurações que dizem respeito ao modelo de dados e suas regras de negócios, que são válidas em qualquer interface de entrada e saída de dados, das configurações visuais que interessam apenas ao Web Framework. Para isso, foram criados do tipos novos de arquivos de definição chamados x-model e x-view.

Para que uma classe de dados possa ser manipulada por meio das APIs Entity e EntitySet, suas definições devem estar exclusivamente em arquivos do tipo x-model.

Como o esforço de revisão das definições de classes não é trivial, foi necessário criar uma sinalização que indique que uma classe de dados foi revista e teve suas definições de modelo e visuais separadas. Para sinalizar que uma classe foi revista, indicamos que ela pode ser avaliada em modo estrito. Essa configuração deve ser realizada classe-a-classe, com a criação do arquivo 0010 StrictMode.config com o conteúdo abaixo:

this.strictMode = true;

Uma vez que o modo estrito está habilitado, a tentativa de utilizar APIs relacionadas às configurações visuais, como as definidas pelas classes ViewDef e ViewDefField, produzirá um erro em arquivos do tipo x-model ou x-class. O uso dessas APIs passa a ser permitido apenas em arquivos do tipo x-view ou em processos e relatórios do Web Framework.

Pela natureza da herança das classes de dados, ativar o modo estrito em uma classe requer que todas as classes ancestrais até a Raiz também estejam em modo estrito.

As validações realizadas pelo modo estrito podem ser desativadas globalmente por meio da propriedade ClassDefManager.strictMode.

Definições de classes personalizadas

Para a eventual necessidade de utilizar recursos das classes que não foram compatibilizadas com o modo estrito, foi disponibilizada a opção de informar um modelo de dados customizado para uma classe de dados por meio da opção modelDef. No exemplo abaixo é utilizada a classe de dados “Servidores” sem os eventos configurados no modelo dados. Desativar os eventos permite utilizar as regras estruturais definidas em uma classe de dados em modo não estrito, sem os eventos que normalmente estão associados à API da grade de dados do Web Framework.

const modelDef = classDefManager.getModelDef(ClassKeys.SERVERS);                              
modelDef.offAll(); // retira todos os listeners de todos os eventos.     
const ds = classes.getCachedDataSet(ClassKeys.SERVERS);            
const entitySet = new EntitySet(ClassKeys.SERVERS, ds, {   
  modelDef: modelDef                                                     
}); 

É importante observar que desligar os eventos possibilita que manipulações de modelo possam ser realizadas sem que as regras de negócio sejam executadas, portanto essa opção não é recomendada e deve ser utilizada apenas quando o controlador assume totalmente a responsabilidade de realizar todas as validações das regras de negócio. Essa funcionalidade deve ser vista como uma opção de transição enquanto não é concluída a revisão de todo o modelo de dados para o modo estrito e que no futuro poderá ser desativada.

Por segurança, o sistema impede por padrão o uso das classes Entity e EntitySet em classes de dados que não estejam em modo estrito. Para desativar essa validação e utilizar classes de dados que não estejam em modo estrito, o desenvolvedor poderá criar um arquivo JavaScript (extensão .ijs) no diretório /Configurações/Inicialização do Roteador HTTP com o conteúdo abaixo:

const Entity = require('@nginstack/orm/lib/Entity');
Entity.requiresStrictMode = false;

Transformações de resultados

O REST Framework permite que sejam registradas funções de transformação de resultados com dois objetivos:

  1. Simplificar os controladores, ao evitar a repetição de códigos e tratamentos comuns as ações.
  2. Versatilidade da resposta em função dos formatos suportados pelo cliente.

Um exemplo é a necessidade de serializar os objetos retornados pelo controlador em JSON. Independente do tipo retornado por um método do controlador, seja ele um DataSet, um Entity, um EntitySet ou um erro emitido, o retorno efetivo da API precisa ser convertido em um JSON. Para isso, o REST Framework registra funções que transformam objetos dessas classes em suas representações JSON.

Uma função de transformação pode alterar outros aspectos da resposta além do conteúdo, como o código da resposta no exemplo abaixo:

RouteResult.addTransformer(function (result, request) {
  if (result.content instanceof PermissionError) {
    return result.withStatus(Status.FORBIDDEN);
  } else {
    return result;
  }
});

Esse é um recurso importante e poderoso que além de permitir simplificações na implementação dos controladores também possibilita que a conversão do tipo do dado seja atrasada para levar em consideração a capacidade final do requisitante. Por exemplo: se um requisitante não suportar JSON e indicar que aceita apenas XML, uma função de transformação pode converter o resultado para XML em vez de JSON. Por esse motivo, os controladores devem retornar objetos de mais alto nível sempre que for possível, como Entity, EntitySet, DataSet, DataSource, em vez de converter manualmente esses tipos para um JSON.

Abaixo segue o exemplo de uma conversão não recomendada para JSON na camada do controlador:

MyUserController.prototype.list = function (request) {
  return this.ok(EntitySet.fromClass(ClassKeys.USERS).toJSONString());
};

O código acima limita a API para comunicação via JSON. Ele pode ser simplificado para a forma abaixo, permitindo que funções de transformação convertam o resultado em outros tipos de mídia além do JSON, com base nos dados da requisição:

MyUserController.prototype.list = function (request) {
  return this.ok(EntitySet.fromClass(ClassKeys.USERS));
};

Funções de transformação podem ser registradas por meio da função RouteResult.addTransformer. As funções de transformação são executadas seguindo a lógica FILO (first in last out), ou seja, as últimas funções de transformação registradas serão as primeiras a serem executadas.

A princípio as transformações padrões do REST já estão contempladas pelo framework. O desenvolvedor só deve adicionar uma nova transformação se o tipo a ser retornado não estiver dentro dos tipos que já sofrem transformações. Por esse motivo, criar novas transformações além das disponibilizadas pelo REST Framework não será algo frequente. Caso sejam necessárias, as funções de transformação devem ser registradas em arquivos de inicialização no diretório /Configurações/Inicialização do Roteador HTTP.

Importante: quando não for especificada uma codificação de forma explícita, os objetos serão convertidos em JSON utilizando a codificação UTF-8, conforme RFC 7159.