-
Como inicializa o projeto MVCBr ?
R. No início no projeto (.dpr) o assistente inclui:
[code]
ApplicationController.Run(TLojaAppController.New,
function: boolean
begin
// retornar True se o applicatio pode ser carregado
// False se não foi autorizado inicialização
result := true;
end);
[/code] -
No formulário principal, como abrir uma janela com outra VIEW?
R. todo VIEW representa um formulário e é controlado por um “controller” – o “controller” é a porta de entrada para inicializar um formulário, assim é necessário criar o “controller”:
[code]
procedure TFormChildSampleView.Button1Click(Sender: TObject);
var aController:IFormChildController;
begin
aController := resolveController<IFormChildController>;
aController.ShowView();
end;
[/code] -
Tenho um MODEL com regras de negócio, como acessar os dados desses MODEL?
R. O primeiro passo é tornar o MODEL visível para o “controller”, visto que o “controller” mantem uma lista de MODEL associado a ele mesmo.
Para instânciar o MODEL dentro do “controller”:
[code]
/// Adicionar os modulos e MODELs personalizados
Procedure TFormChildSampleController.CreateModules;
begin
// adicionar os seus MODELs aqui
// exemplo: add( MeuModel.new(self) );add( TRegrasNegociosModel.new(self) );
end;
[/code]
Na view você pode usar o “controller” para chamar o “MODEL”. -
Como chamar um MODEL de dentro de um VIEW?
R. O MODEL é inicializado junto com o “controller” que mantem uma lista de MODELs ativos para acesso.
[code]
procedure TFormChildSampleView.Button2Click(Sender: TObject);
var
AModel:IRegrasNegociosModel;
begin
AModel := GetModel<IRegrasNegociosModel>;
AModel.Validar(‘Show Model Validar()’);
end;
[/code] -
Quero embutir um VIEW dentro de um TABSHEET, como posso acessar os atributos do formulário ?
R. você precisa inicialmente carregar o “controller” do VIEW – isto vai permitir acesso ao GETVIEW.This que retorna a instância do TFORM;
[code]
procedure TFormChildSampleView.Init;
var
ACtrl:IEmbededFormController;
AForm:TForm;
begin
// incluir incializações aqui// embeded form…
ACtrl := resolveController<IEmbededFormController>;
AForm := TForm(ACtrl.GetView.This);
AForm.parent := TabSheet1;
AForm.BorderStyle := bsNone;
AForm.Align := alClient;
AForm.Show;end;
[/code]
-
Quando trabalho com FMX é comum utilizar TABCONTROL ou TLAYOUT para mostrar VIEWs, como um VIEW pode enviar uma mensagem para outro VIEW que já esta carregada ?
R. para enviar uma mensagem para um VIEW específico, você precisa da “interface” do VIEW desejado – o que pode ser feito incluindo no USES da UNIT uma chamada para a UNIT de “interface” do VIEW.
[code]
procedure TFormChildSampleView.Button4Click(Sender: TObject);
begin
ApplicationController.ViewEvent(IEmbededFormView,’Message to: EmbededFormView’);
end;
[/code]
Outra forma é se você conhece o “controller” alvo, pode enviar a mensagem diretamente para o “controller”.
[code]
procedure TFormChildSampleView.Button5Click(Sender: TObject);
begin
ResolveController<IEmbededFormController>.ViewEvent(‘Message via controller’);
end;
[/code]
Para mandar mensagem para todos os VIEW é possível utilizar o ApplicationController:
[code]
procedure TFormChildSampleView.Button3Click(Sender: TObject);
begin
applicationController.ViewEvent( ‘generic message to all VIEW’ );
end;
[/code]
Mês: março 2017
Firebird3 SQL com acesso através do protocolo OData via RESTful
Nestes primeiros meses de 2017, o grupo MVCBr dedicou a maior parte do tempo em implementar um servidor OData que permite o acesso a base de dados utilizando protocolo RESTful via HTTP.
FIREBIRD !!!
Sim….. o servidor OData implementado no MVCBr é um servidor que expõe recursos (resources) armazenados em um servidor FIREBIRD 3.0
Simplificando, o servidor MVCBrServer é um servidor RESTful que utiliza o protocolo OData que suporta o Firebird3.
Arquitetura do Servidor
O servidor é um servidor implementado utilizando componentes INDY para fazer o processamento das requisições. Ao receber a chamada do CLIENTE o motor INDY passa para o framework “Delphi MVC Framework” implementado pelo “Daniele Teti”. Seguindo os padrões RESTful, o servidor analisa o tipo de pacote que esta recebendo (GET, PUT, POST, PATCH, DELETE, OPTIONS) e passa a requisição para um PARSER OData implementado no framework MVCBr. Ao receber a requisição o PARSER prepara a requisição que será feito ao banco de dados (ex: select * from produtos), avalia o “metadata” contendo as regras de acesso e constrói a solicitação através da estrutura do FireDAC do Delphi, que remete ao driver o FIREBIRD. Recebendo o retorno tudo é empacotado utilizando JSON e devolvido para o cliente.
O “metadata” – Onde o modelo relacional é descrito
Junto com o projeto MVCBr irá encontrar um banco FIREBIRD3 (MVCBr.fdb) que possui uma estrutura básica de teste utilizada no “framework”
Configurar o “databases.conf” do firebird:
# # Live Databases: # mvcbr=[path]/mvcbr.fdb
Como configurar um resource
Resource é o identificar a ser utilizado no HTTP para acessar um determinado recurso do banco de dados.
Veja o exemplo:
{ "resource": "produtos", "collection": "produtos", "keyID": "codigo", "maxpagesize": 100, "fields": "*", "method": "GET,PATCH,POST,PUT,DELETE", "relations": [ { "resource": "grupos", "sourceKey": "grupo", "targetKey": "codigo", "join": "left join grupos on (produtos.grupo=grupos.grupo)" } ] }
- resource: apelido para o URL utilizada no HTTP
- collection: nome da tabela fisica no banco de dados
- keyID: coluna de acesso rápido ás linhas da tabela ex: http://…./OData/OData.svc/produtos(‘789112313311’)
- maxpagesize: número máximo de linhas a retornar caso não seja indicado o comando $top
- fields: lista de colunas a retornar quando o comando $select não for indicado
- method: quais as permissões serão publicadas aos clientes
- relations: quais relacionamento podem ser executados com “resource” corrente ( é um DETAIL)
- relations.resource: qual o apelido do relacionamento com o resource MASTER
- relations.sourceKey: qual a coluna de recionamento no resource MASTER
- relations.targetKey: qual a coluna de relacionamento no resource DETAIL
- relations.join: utilizado para JOINs mais complexos ignorando “sourceKey” e “targetKey”
Listagem completa do metadata de exemplo
[code lang=”javascript”]
{
"@odata.context": "http://localhost:8080/OData/OData.svc",
"__comment": "Services list all resource available to OData.Service",
"OData.Services": [
{
"resource": "produtos",
"collection": "produtos",
"keyID": "codigo",
"maxpagesize": 100,
"fields": "*",
"method": "GET,PATCH,POST,PUT,DELETE",
"relations": [
{
"resource": "grupos",
"sourceKey": "grupo",
"targetKey": "codigo",
"join": "left join grupos on (produtos.grupo=grupos.grupo)"
}
]
},
{
"resource": "grupos",
"collection": "grupos",
"keyID": "codigo",
"fields": "*",
"method": "GET,PATCH,DELETE,PUT,POST",
"maxpagesize": 100,
"relations": [
{
"resource": "produtos",
"sourceKey": "codigo",
"targetKey": "grupo",
"join": "join produtos on (grupos.codigo=produtos.grupo)"
}
]
},
{
"resource": "fornecedores",
"collection": "fornecedores",
"maxpagesize": 100,
"fields": "*",
"keyID": "codigo"
},
{
"resource": "clientes",
"collection": "clientes",
"keyID": "codigo",
"method": "GET,POST,PATCH,UT,DELETE",
"searchFields": "nome",
"maxpagesize": 100,
"fields": "*"
"relations": [
{
"resource": "vendas",
"join": "join vendas on (vendas.cliente=clientes.codigo)"
},
{
"resource": "vendas_item",
"join": "join vendas a on (clientes.codigo=a.cliente) join vendas_item b on (b.documento=a.documento)"
}
]
},
{
"resource": "vendas",
"collection": "vendas",
"maxpagesize": 100,
"keyID": "documento",
"fields": "*",
"method": "GET,POST,PATCH,PUT,DELETE"
},
{
"resource": "vendas_item",
"collection": "vendas_item",
"maxpagesize": 100,
"keyID": "documento"
"method": "GET,POST,PATCH,PUT,DELETE"
}
]
}
[/code]
MVCBr – Como criar um CRUD com OData
Chegar a um CRUD é um grande desafio. Associar um MVC no desenvolvimento e a utilização de RESTful para persistir os dados (com OData) só faz crescer o desafio.
O desafio vem do fato de tudo ser novidade e forçar a quebrar paradigmas antes impensáveis utilizando a ferramenta do dia-a-dia, o Delphi.
Vamos ligar os componentes:
TIdHTTPRestClient é responsável em fazer a
comunicação entre o cliente e o servidor RESTful.
É o canal para buscar dados no servidor, bem como enviar dados para o servidor (é um componente de transporte).
Os preenchimento das propriedades do RestClient pode ser feito manualmente, ou ainda, de forma automática se for utilizado em conjunto com um TODataBuilder.
Com o TODataBuilder permite construir a soliciatação GET a ser enviada ao servidor OData.
Ligar ao TODataBuilder a propriedade RestClient com o TIdHTTPRestClient. Com isto o comando construído no Builder será repassado (preenchido automático) no RestClient. ver mais….
O passo seguinte é ligar o TODataDatasetAdpater.
O Adapter recebe os dados do RestClient e liga com o Dataset.
Ligar o Builder, Dataset e o ResponseJSON( RestClient).
Para obter os dados com os servidor, chamar o método execute. ver mais…
Feito estas ligações nos componentes, já é possível chamar o EXECUTE do Adapter que irá preencher o Dataset com a resposta do servidor.
Para enviar um INSERT, DELETE ou UPDATE para o servidor ( ver exemplo).
O código a seguir mostra como montar os dados a serem enviado para o servidor persistir as alterações.
Os seguintes métodos do Adapter são importantes, como no exemplo:
ODataDatasetAdapter1.AddRowSet(rctInserted, DataSet); /// põe na pilha um INSERT; ODataDatasetAdapter1.AddRowSet(rctModified, DataSet); /// põe na pilha um UPDATE ODataDatasetAdapter1.AddRowSet(rctDeleted, DataSet); /// põe na pilha um DELETE; ODataDatasetAdapter1.ApplyUpdates(nil, rmPATCH); /// atualiza o servidor com os dados da pilha; ODataDatasetAdapter1.Execute; /// faz um SELECT no banco de dados ODataDatasetAdapter1.ClearChanges; /// limpa a pilha
Código de exemplo que esta no projeto MVCBr. no git
[code lang=”pascal”]
{ //************************************************************// }
{ // // }
{ // Código gerado pelo assistente // }
{ // // }
{ // Projeto MVCBr // }
{ // tireideletra.com.br / amarildo lacerda // }
{ //************************************************************// }
{ // Data: 10/03/2017 21:21:54 // }
{ //************************************************************// }
///
/// Uma View representa a camada de apresentação ao usuário
/// deve esta associado a um controller onde ocorrerá
/// a troca de informações e comunicação com os Models
….
DBGrid1: TDBGrid;
DataSource1: TDataSource;
/// Componente de Envio de mensagem HTTP para o servidor OData RESTful
IdHTTPRestClient1: TIdHTTPRestClient;
/// Construção de URL a ser enviado ao servidor
ODataBuilder1: TODataBuilder;
/// Desempacota a resposta do servidor e preenche o Dataset
ODataDatasetAdapter1: TODataDatasetAdapter;
/// Dataset que recebe a resposta do servidor – preenchida com o Adpater
FDMemTable1: TFDMemTable; /// FireDAC
Button1: TButton; /// botão GET
Button2: TButton; /// botão ApplayUpdates
….
private
FLoading: boolean; /// Flag para marcar se esta fazendo carga inicial da tabela.
FState: TDataSetState; /// Guarda o STATE do Dataset a ser usando no AfterPost
protected
public
{ Public declarations }
…
end;
Implementation
{$R *.DFM}
…..
/// No evento AfterPost do Dataset, armazena na lista de
/// alterações a serem enviadas para o servidor OData RESTful
procedure TRestODataAppView.FDMemTable1AfterPost(DataSet: TDataSet);
begin
if not FLoading then /// trata somente os registros alterados pelo usuário… (para não pegar os dados iniciais na carga da resposta do servidor)
case FState of
dsInsert: /// empilha os dados de INSERT – inserted
ODataDatasetAdapter1.AddRowSet(rctInserted, DataSet);
dsEdit: /// empilha os dados do POST – modified
ODataDatasetAdapter1.AddRowSet(rctModified, DataSet);
end;
end;
/// No ApplyUpdates do Dataset despachar para o servidor
/// as alterações armazenadas para serem notificadas
/// ao servidor
procedure TRestODataAppView.FDMemTable1BeforeApplyUpdates(DataSet: TFDDataSet);
begin
// envia os dados alterados para o servidor OData RESTful
ODataDatasetAdapter1.ApplyUpdates(nil, rmPATCH);
end;
procedure TRestODataAppView.FDMemTable1BeforeDelete(DataSet: TDataSet);
begin
/// empilha os dados a serem de DELETE – deleted a ser enviado ao servidor
ODataDatasetAdapter1.AddRowSet(rctDeleted, DataSet);
end;
procedure TRestODataAppView.FDMemTable1BeforePost(DataSet: TDataSet);
begin
/// guarda o estado da linha (dsEdit, dsInsert) para uso no AfterPost
FState := DataSet.State;
end;
procedure TRestODataAppView.FormCreate(Sender: TObject);
begin
/// um exemplo de como adicionar CustomHeaders a mensagem que será enviada ao servidor
IdHTTPRestClient1.IdHTTP.Request.CustomHeaders.AddValue(‘token’, ‘abcdexz’);
end;
procedure TRestODataAppView.Button1Click(Sender: TObject);
begin
FLoading := true; /// controle para não gerar linhas de alterações na pilhas
try
/// executa um GET no servidor RESTful
/// o servidor fazer um SELECT e devolve o conteúdo as linhas do SELECT
ODataDatasetAdapter1.Execute;
/// limpa alterações que estejam armazenadas
ODataDatasetAdapter1.ClearChanges;
finally
FLoading := false;
end;
end;
procedure TRestODataAppView.Button2Click(Sender: TObject);
begin
FDMemTable1.ApplyUpdates(); /// processa a alteração do Dataset
end;
[/code]
TODataDatasetAdapter – Um adaptador OData para Dataset
O componente TODataDatasetAdapter é um construtor associado ao Dataset convertendo o JSON enviado pelo servidor em um DATASET navegável no formulário. Vejamos suas propriedades.
Onde:
Builder: ligação para o TODataBuilder;
Datasert: ligação para um TFDMemTable (aka. TClientDataset !!!);
ResponseJSON: ligação com o TIdHTTPRestClient;
ResponseType: no momento só aceita texto plano sem compactação – pureJSON;
RootElement: caminho onde se encontra o ARRAY de valores no JSON enviado pelo servidor (no OData o padrão é “value”);
Introdução ao OData | TODataBuilder | TIdHTTPRestClient
MVCBr – TODataBuilder, um construtor de requisições OData
TODataBuilder é um construtor de URI para chamada ao OData Server.
O construtor é uma facilidade para criar a URI de chamada ao OData através da interface gráfica do Delphi onde:
- BaseURL – Servidor e porta do OData;
- Count – indica se é para retornar o número total de linhas do select – usado para paginação;
- Expand – lista de Resource DETAIL (não implementado no MVCBrServer neste momento);
- Filter – filtro de contrução auxiliar para o cláusula WHERE de busca no banco de dados (aceita os operadores do OData);
- Resource – lista de Collections/Tabelas e respectivos parâmetros aninhadas ( max 3 );
- Select – lista de colunas a retornar pelo consulta no servidor;
- Service – nome do serviço no servidor;
- ServicePreffix – prefixo de composição da URI associado ao Serviço;
- SkipRows – indica quantas linhas saltar antes de retornar o conjunto de linhas (default=0);
- TopRows – indica a quantidade de linhas a serem retornadas (default=0 todas);
O componente expõe o metódo “execute” que tem por atribuição preencher os parâmetros do TIdHTTPRestClient e chamar o seu “execute”;
RestClient | Introdução ao OData
MVCBr – TIdHTTPRestClient – Um RestClient com Indy IdHTTP
TIdHTTPRestClient é um cliente RESTful implementado sobre o componente INDY TIdHTTP de uso geral.
O procedimento “execute” do componente envia uma solicitação ao servidor HTTP e aguarda resposta pela propriedade “content”.
Propriedades
- BaseURL – servidor e porta de acesso;
- Method – identifica o método a ser executa no servidor;
- Resource – URL de acesso;
- ResourcePreffix – Serviço de acesso no servidor;
Informações adicionais de HEADER deve ser enviado através da propriedade do componente “IdHTTP” que expõe o componente TIdHTTP instanciado internamente.
Exemplo de envio de “token” no HEADER:
IdHTTPRestClient1.IdHTTP.Request.CustomHeaders.AddValue('token','abcdexz');
Se necessário obter informações adicionais é possível obter a partir de documentação do TIdHTTP e pode ser aplicado a propriedade IdHTTPRestClient1.IdHTTP…
MVCBr – INSERT, DELETE ou UPDATE em um mesmo comando
Nos textos anteriores foi possível destacar como fazer INSERT, DELETE e UPDATE – cada um individualmente em comandos separados.
Aqui vamos mostrar o METHOD PATCH. Na especificação padrão do RESTful o PATCH efetua um UPDATE no banco de dados. Qual então a diferença entre PUT e PATCH ? Regra geral no OData, o comando PUT faz update de todas as colunas da tabela, enquanto o PATCH faz igualmente um update, no entanto realiza atualização somente nas colunas indicadas no BODY.
No MVCBrServer, o PUT e o PATCH possuem as mesmas funcionalidades, ou seja, atualizam somente as colunas indicadas no BODY como já visto no texto que discorremos sobre o METHOD PUT.
Uma variação implementada no MVCBrServer é a possibilidade do comando PATCH executar múltiplas funções e aceitar enviar INSERT, DELETE e UPDATE dentro de um mesmo ARRAY com as linhas de comando.
Para executar múltiplas funcionalidade, cada linha deve ser indicada com uma propriedade “rowstate” com um dos valores: “modified”, “inserted”, “deleted”.
Exemplo:
url: http://localhost:8080/OData/OData.svc/vendas() body: [ { "rowstate":"deleted","data":"2017-03-17","documento":3}, { "rowstate":"inserted","data":"2017-03-17","documento":3,"cliente":1,"total":10}, { "rowstate":"modified","data":"2017-03-17","documento":3,"cliente":2,"total":10} ]
Este BODY envia 3 comandos no mesmo ARRAY que serão executados na seguinte ordem:
- na linha 1 o “rowstate”:”deleted” indica que a linha deve ser processada como um comando DELETE;
- na linha 2 o “rowstate”:”inserted” indica que a linha é um INSERT;
- na linha 3 o “rowstate”:”modified” deve processar um UPDATE para a linha;
Resposta: {"@odata.context":"\/OData\/OData.svc\/vendas()","StartsAt":"2017-03-17T20:45:46.541Z", "@odata.count":"3","EndsAt":"2017-03-17T20:45:46.559Z"}
Em relação ao controle de transação da operação o controle ocorre no mesmo formato que já tratamos aqui ACID , ou seja – tudo ou nada.
MVCBr – Controle de Transação com MVCBrServer / OData
O protocolo OData não trata sobre o controle de transação do banco de dados. Esta é na verdade um decisão do server que implementação será feita. Como o OData nasce em um ambiente noSQL com forte presença é fácil imaginar que o controle de transação é uma preocupação mais presente no legado e menos presente nas novas tecnologia.
Aqui um paradigma bom a ser vencido ao longo dos anos – como sair de um ambiente transacional e abandonar o ACID no noSQL…
Aqui no MVCBrServer o banco de transação é o Firebird e a transação faz parte da estrutura de controle. Pensando nisto, quando o cliente solicita um conjunto de linhas em um ARRAY o server faz um STARTTRANSACTION e somente após a conclusão de todas as linhas irá enviar um COMMIT para o servidor.
Caso ocorra algum erro durante o processo um ROLLBACK é enviado para o servidor cancelando todo o lote da transação.
MVCBr – Method PUT para enviar UPDATE com protocolo OData
A execução de um UPDATE no banco de dados envolve duas categorias de informações. A primeira delas é uma lista de colunas a atualizar no servidor, na segunda parte de importância é a indicação de quais linhas serão envolvidas na atualização (a WHERE).
Com base na especificação OData para RESTful, o METHOD PUT indica que o cliente deseja fazer uma atualização da tabela. As colunas a serem atualizadas devem ser enviadas no BODY da mensagem ao servidor RESTful. O servidor MVCBrServer esta preparado para receber uma linha simples de colunas/valores, bem como um ARRAY com um conjunto de linhas.
Possibilidades de formato do BODY para atualizar a(s) linha(s):
- Uma linha simples:
{ "id" : "1" , "nome":"DESCRICAO TESTE" }
- Um ARRAY com uma lista de linhas:
[ { "id": "1", "nome": "DESCRICAO 1"}, { "id": "2", "nome": "DESCRICAO 2"},.... ]
Se o BODY indica os valores das colunas a serem atualizadas no servidor, próximo passo é tratar a seleção das linhas que iram receber atualização já que em geral queremos atualizar uma linha ou um conjunto delas e raramente desejamos atualizar todas as linhas.
Preferencialmente a definição da WHERE deve ser indicada como parâmetro para o RESOURCE (a tabela ou coleção):
http://localhost:8080/OData/OData.svc/produtos(id='1') -> aplica atualiza sobre a linha com ID = "1" caso o RESOURCE requeira mais de uma coluna - pode-se separa-las por vírgulas http://localhost:8080/OData/OData.svc/produtos(id='1',outra='x') -> aplica atualiza sobre a linha com ID = "1" AND outra="x"
Caso a coluna não seja indicada, o MVCBrServer irá utilizar a chave indicada no metadata (modelo de dados).
Quando o BODY indica uma lista de linhas em um ARRAY – não é possível indicar a chave nos parâmetros já que cada linha possui uma chave diferente (WHERE). Neste caso o servidor irá utilizar o seu “keyID” do metadata e aplicar o valor de cada linha na WHERE para escolher as linhas que irão receber atualização ou seja, as linhas enviadas no ARRAY devem conter as colunas correspondente a chave do RESOURCE.
Ao caso cabe uma questão – A especificação OData indica que no comando PUT, todas as colunas devem ser enviadas para o servidor. A implementação no MVCBrServer não possui esta exigência – é possível enviar somente as colunas da chave e as colunas a serem atualizadas, ignorando as colunas que não foram enviadas na lista. Nos casos que for necessário enviar um NULL para uma coluna, este desejo deve constar na linha enviada… Ex: {…., “colunaxxx”: NULL, … }
Introdução ao OData | INSERT | DELETE |
MVCBr Usando OData para DELETE de registros.
O texto anterior mostrei um exemplo de como enviar um INSERT para o servidor usando o METHOD POST padrão RESTful utilizado no MVCBrServer.
Agora vamos olhar como enviar comando DELETE (METHOD DELETE) para o servidor e excluir um linha na tabela.
Há duas possibilidade para excluir registros de uma tabela na estrutura do servidor MVCBrServer. A primeira é enviar uma única linha diretamente pela URI ao servidor indicando o RESOURCE e como parâmetro o identificador padrão da linha – em geral ligado a chave primária:
- http://localhost:8080/OData/OData.svc/grupos(’08’) – com METHOD DELETE, exclui a(s) linha(s) com chave ’08’ – neste formato o servidor irá utilizar como chave para encontrar a linha a excluir o “keyID” da tabela indicado no metadata – Um cuidado a observar é que neste formato pode ocorrer do metadata se referir a mais de uma linha e neste caso o servidor irá excluir todas as linhas que contenham a mesma informação – exemplo: se for o documento da venda e o RESOURCE for os itens irá excluir todos os itens da venda que contenham o mesmo documento;
- http://localhost:8080/OData/OData.svc/grupos(grupo=’08’) – neste modelo o servidor irá utilizar a coluna indicada para selecionar as linhas a serem escolhidas o que permite melhor controle pelo “Coder”. Caso seja necessário mais de uma coluna, basta separar por vírgula ex: …/ItemDaVenda(dcto=’00001′,ordem=1);
Outro formato é o envio no “BODY” com um ARRAY contendo uma lista de linhas a serem excluídas:
http://localhost:8080/OData/OData.svc/ItemDaVenda no BODY enviar o JSONArray: [ { "dcto":"00001", "ordem":1}, { "dcto":"00002", "ordem":1} .... ]