Share Button

Um servidor Datasnap é na excência um servidor HTTP nativo…. SIM é possível implementar um HTTP Server no mesmo servidor de Datasnap.

1. Criar um Servidor Datasnap padrão

Ao criar um Servidor DataSnap (pelo expert do Delphi), várias UNITs são adicionadas.
Se observar a UNIT que nos importa aqui é a WebModuleUnit1;

Toda vez que é feito uma chamada a um recurso na porta do HTTP do servidor e for um arquivo será executado o evento “WebModuleBeforeDispatch” o que permite inserir códigos que execute algum procedimento diferente daqueles previstos.

No nosso caso queremos chamar um arquivo externo “index.html”, “jquery.js”, “minhafoto.png”, “my.pdf”… enfim, um arquivo que esteja armazenado na pasta base do site “www”.

2. Inserindo código para suporte ao HTTP Server

Tentando facilitar o trabalho introduzi um novo componente através da  “UNIT DataSnap.HTTPModule;”


// incluir na USES
uses ... DataSnap.HTTPModule...
..

// Incluir um Private para o objeto 
   {Componente que implementa o código de resposta para o HTTP Server - por Amarildo Lacerda}
   FHttpModule:TDataSnapHTTPModule;
..

// alterar o Create para
procedure TWebModule1.WebModuleCreate(Sender: TObject);
begin
  {
     incia o componente indicando a pasta onde será hospedado a URLBase para oa arquivos de HTTP Server
     [pastaBase]/index.html
  }
  FHttpModule:=TDataSnapHTTPModule.Create(self);
  FHttpModule.URLPath := ExtractFilePath(paramStr(0)) + 'www';
  ForceDirectories(FHttpModule.URLPath);

  FServerFunctionInvokerAction := ActionByName('ServerFunctionInvokerAction');
end;

// alterar 
procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  { Redireciona a chamada HTML, JavaScript ou outro tipo de arquivo que exista na pasta padrão - por Amarildo Lacerda}
  FHttpModule.GoURLDocument(Request, Response, Handled);
  if Handled then
    exit;


  if FServerFunctionInvokerAction <> nil then
    FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
end;

Pronto… Seu Servidor Datasnap já sabe como responder como um HTTP Server nativo.

Digite: http://localhost:8080 – e o servidor irá procurar por uma página padrão: index.html

Código de Exemplo: Datasnap.HttpModule

Share Button

Estava trabalhando para simplificar o TRESTClient do DataSnap  e implementar alguns recursos que gostaria de ter e não encontrei no componente distribuido com o delphi ( Package para TRESTSocialClient ). Mapear as respostas Array para um TFDDataset primordial para facilitar o desenvolvimento (ex: TRESTSocialClientDataset). E quando não é possível mapear para um Dataset… código+código+bug+complexo…

Quando trabalho com um Servidor REST Datasnap escrito para delphi – a integração é simples utilizando o “Wizard” para criar um projeto REST Client disponível. Quando o servidor REST responde para múltiplas plataformas é imperativo que o servidor envie respostas “padronizadas” que sejam compatíveis com a plataforma que o cliente utiliza. O caminho mais rápido é responder JSON padrão evitando respostas específicas de uma plataforma ou outra.

Exemplo: um método não pode responder com um TDataset.Data como é o indicado pelo Delphi… isto obrigaria que o cliente seja igualmente um Delphi.. Então para manter padrão o método deve responder com um TJSONValue ou TJSONObject.

Tomando por base o Servidor Datasnap do post “FireDAC – Datasnap” onde publica o método  GetCliente/{cnpj} (http://localhost:8080/datasnap/rest/TServerMethods1/GetCliente/123456) que retorna:

{"result":[{"cliente":[{"RowId":1,"codigo":1,"nome":"Embarcadero SA","cidade":"Sao Paulo","estado":"SP","endereco":"Rua…xxxx…,10","debitos":100000.12},{"RowId":2,"codigo":2,"nome":"Embarcadero SA2","cidade":"Sao Paulo","estado":"SP","endereco":"Rua…xxxx…,10","debitos":100000.12}],"adicional":[{"codigo":1,"nome":"Exemplo 2"}]}]}

Nas andanças pelos BLOGs de MVPs encontrei um projeto “Introducing JsonToDelphiClass” que aponta para o repositório com o código do projeto.

O projeto JsonToDelphiClass permite que a representação JSON de retorno do servidor DataSnap seja colado no espaço para texto indicada e usar o botão de geração da Unit contendo a classe Delphi para a representação JSON.

As classes geradas pelo projeto possuem um método que fornecendo o código de retorno do servidor – popula o objeto delphi – facilitando a conversão da estrutura JSON para Classe.

class function FromJsonString(AJsonString: string): T... (criar a classe e popula)

.

Passando aquele retorno do Servidor, veja como ele construiu as classes para a representação JSON:

unit JsonRestServer;

//  *************************************************
//    Generated By: JsonToDelphiClass - 0.65
//    Project link: https://github.com/PKGeorgiev/Delphi-JsonToDelphiClass
//    Generated On: 2016-04-10 15:41:06
//  *************************************************
//    Created By  : Petar Georgiev - 2014
//    WebSite     : http://pgeorgiev.com
//  *************************************************

interface

uses Generics.Collections, Rest.Json;

type

TAdicionalClass = class
private
  FCodigo: Extended;
  FNome: String;
public
  property codigo: Extended read FCodigo write FCodigo;
  property nome: String read FNome write FNome;
  function ToJsonString: string;
  class function FromJsonString(AJsonString: string): TAdicionalClass;
end;

TClienteClass = class
private
  FRowId: Extended;
  FCidade: String;
  FCodigo: Extended;
  FDebitos: Extended;
  FEndereco: String;
  FEstado: String;
  FNome: String;
public
  property RowId: Extended read FRowId write FRowId;
  property cidade: String read FCidade write FCidade;
  property codigo: Extended read FCodigo write FCodigo;
  property debitos: Extended read FDebitos write FDebitos;
  property endereco: String read FEndereco write FEndereco;
  property estado: String read FEstado write FEstado;
  property nome: String read FNome write FNome;
  function ToJsonString: string;
  class function FromJsonString(AJsonString: string): TClienteClass;
end;

TResultClass = class
private
  FAdicional: TArray<TAdicionalClass>;
  FCliente: TArray<TClienteClass>;
public
  property adicional: TArray<TAdicionalClass> read FAdicional write FAdicional;
  property cliente: TArray<TClienteClass> read FCliente write FCliente;
  destructor Destroy; override;
  function ToJsonString: string;
  class function FromJsonString(AJsonString: string): TResultClass;
end;

TRootClass = class
private
  FResult: TArray<TResultClass>;
public
  property result: TArray<TResultClass> read FResult write FResult;
  destructor Destroy; override;
  function ToJsonString: string;
  class function FromJsonString(AJsonString: string): TRootClass;
end;

implementation

{TAdicionalClass}


function TAdicionalClass.ToJsonString: string;
begin
  result := TJson.ObjectToJsonString(self);
end;

class function TAdicionalClass.FromJsonString(AJsonString: string): TAdicionalClass;
begin
  result := TJson.JsonToObject<TAdicionalClass>(AJsonString)
end;

{TClienteClass}


function TClienteClass.ToJsonString: string;
begin
  result := TJson.ObjectToJsonString(self);
end;

class function TClienteClass.FromJsonString(AJsonString: string): TClienteClass;
begin
  result := TJson.JsonToObject<TClienteClass>(AJsonString)
end;

{TResultClass}

destructor TResultClass.Destroy;
var
  LclienteItem: TClienteClass;
  LadicionalItem: TAdicionalClass;
begin

 for LclienteItem in FCliente do
   LclienteItem.free;
 for LadicionalItem in FAdicional do
   LadicionalItem.free;

  inherited;
end;

function TResultClass.ToJsonString: string;
begin
  result := TJson.ObjectToJsonString(self);
end;

class function TResultClass.FromJsonString(AJsonString: string): TResultClass;
begin
  result := TJson.JsonToObject<TResultClass>(AJsonString)
end;

{TRootClass}

destructor TRootClass.Destroy;
var
  LresultItem: TResultClass;
begin

 for LresultItem in FResult do
   LresultItem.free;

  inherited;
end;

function TRootClass.ToJsonString: string;
begin
  result := TJson.ObjectToJsonString(self);
end;

class function TRootClass.FromJsonString(AJsonString: string): TRootClass;
begin
  result := TJson.JsonToObject<TRootClass>(AJsonString)
end;

end.

 

 

 

Share Button

Excluir uma chave primária quando se tem uma ferramenta visual a frente não é uma operação complicada de se fazer no Firebird.
O problema mesmo… é quando precisamos fazer isto por um script…
O Firebird usa um mapeamento de nomes para a chave primária que cria – diferente do nome do índice e passa a fazer parte da lista de “constraints” da tabela.
Para excluir uma “constraint” é necessário descobrir o seu nome e aplicar a “DROP” da constrait. A seguir código para um procedimento que procura o nome da “constraint” e executa o “DROP” da mesma.

Como usar: Execute Procedure RDB$DELETE_PRIMARYKEY(‘produtos’);


SET TERM ^;

CREATE OR ALTER PROCEDURE RDB$DELETE_PRIMARYKEY (
    tabela varchar(128))
as
declare variable nome varchar(128);
declare variable d  varchar(128);
declare variable stm varchar(1024);
begin

-- localiza o nome da constraint
select rdb$constraint_name from rdb$relation_constraints
where rdb$relation_name= UPPER(:tabela)
     AND rdb$constraint_type='PRIMARY KEY'
into :nome;

-- monta o comando
stm = 'ALTER TABLE '||:tabela||' DROP CONSTRAINT '|| coalesce(:nome,'INDEF');


  if (:nome is not null) then
  begin
    execute statement stm;
  end else
    exception erro 'Nao encontrei a chave primaria para apagar';

  suspend;
end^

SET TERM ; ^