Este artigo é uma continuação do anterior, Se você não leu, faça agora para entender o contexto desse.
Objetivo
O objetivo deste projeto é implementar WebServices REST+JSON em Delphi na versão PROFESSIONAL a partir do Delphi XE2 e posteriores :
- Sem a utilização de Datasnap,
- Sem outros frameworks externos (free ou não)
- Apenas utilizando o INDY que vem com o Delphi em todas as
versões.
Definição de Rest
Segundo a WikiPedia :
O termo REST se referia, originalmente, a um conjunto de princípios de arquitetura (descritos mais abaixo), na atualidade se usa no sentido mais amplo para descrever qualquer interface web simples que utiliza XML e HTTP (ou YAML, JSON, ou texto puro), sem as abstrações adicionais dos protocolos baseados em padrões de trocas de mensagem como o protocolo de serviços Web SOAP
O Web Service apresentado aqui possui várias características de um REST Web Service são elas :
- Stateless (não guarda estado após a chamada)
- Representação de recursos através de JSON/XML
- Provê troca de mensagens entre Requisitante e Requisitado
- Recursos endereçáveis pela URI
Mas não provê :
Veja os requisitos para ter um Rest Webservice completo
Rest orientado a Atividade X orientado a Recursos
Neste projeto foi adotado a orientação a atividade porém usando elementos da orientação a recursos (operações GET/PUT/POST/DELETE) foi feito um mix do dois conceitos.
A Estrutura do Projeto
O exemplo será um cadastro de alunos (CRUD) bem simples, sem validações, sem checagem de erros, porque o objetivo aqui é somente mostrar as operações REST.
Foi implementado um “mini-framework” de trabalho, ou seja é necessário encaixar os seus novos métodos nos locais adequados dentro da estrutura montada.

Observe que temos um form Principal, uma Unit de Tipos (SysTypes), um unit com rotinas de auxílio (ServerUtils) e a principal onde iremos trabalhar, a ServerMethodsUnit1.pas
Na Unit ServerMethodsUnit1.pas temos a classe TServerMethods1 já com alguns ServerMethods implementados, são eles :
| ServerMethod |
OPERAÇÃO |
| ConsultaAluno |
GET |
| GetListaAlunos |
GET |
| AtualizaAluno |
POST |
| InsereAluno |
PUT |
| ExcluiAluno |
DELETE |
Obs : Para essa simulação, não estamos usando banco de dados, para facilitar o teste e evitar que se instale servidores de banco de dados ou criar tabelas, usamos arquivo texto e uma stringlist apenas para demonstrar as operações REST.(GET/PUT/POST/DELETE)
Entendendo a estrutura
Para cada operação (GET/PUT/POST/DELETE) temos um método público na classe TServerMethods da unit ServerMethodsUnit1 :

Para inserirmos um novo método em nosso REST webservice precisamos codificar o próprio método, veja abaixo o server method que retorna a lista de alunos cadastrados :

E depois colocar dentro de CallGETServerMethod, a chamada para ele, seguindo o padrão existente :

O Método CallGetServerMethod será chamado pela engine do framework para toda requisição GET vinda de um client qualquer, desde que este client especifique que a operação é um GET.
O método CallGetServerMethod e os seus primos:
- CallPUTServerMethod
- CallPOSTServerMethod
- CallDELETEServerMethod
São rotinas wrappers onde você encaixa a chamada para o Server Method que você criar.
Pelo padrão adotado, primeiro ele testa Argumentos[0] que deverá chegar aqui com o nome do server method, ele testa também a quantidade de argumentos passados para um dado Server Method, caso a quantidade de argumentos passados não seja a que ele espera receber, retorna uma string JSON indicando erro, quem define a quantidade de argumentos é você.
O array Argumentos sempre conterá o nome do método e em seguida os argumentos, portanto exemplificando para um serverMethod que tenha 2 argumentos, teremos Length(Argumentos) = 3
Se a quantidade de argumentos estiver correta, chame o Server Method passando os parâmetros vindos do Array Argumentos.
A cada novo método será necessário codificar apenas esses 2 pontos,
- o método em si
- e a chamada dele em um dos métodos abaixo: CallGetServerMethod, CallPUTServerMethod, CallPOSTServerMethod ou CallDELETEServerMethod conforme for a operação desejada.
a complexidade acaba aqui, é só isso que precisa fazer, o resto a engine do framework faz pra você.
O framework nada mais é do que o processamento do evento OnGetCommand e OnCommandOther do idHttpServer cuidando de separar cada operação e argumentos, isso pode ser visto no frmMain da aplicação.
Convenções
Antes de codificar estabelecemos algumas convenções de URL :
A URL de chamada deve ser no padrão : http://nomedominio:8080/nomedometodo/argumento1/argumento2/argumentoN
- Onde nomedometodo é o nome do Server Method que que queremos chamar
- e Argumentos são os parâmetros que desejamos passar, sendo variável esta parte, dependendo do método pode ter nenhum, 1 ou mais parâmetros
Pronto para o teste
Para testar o framework fiz uma aplicação no Delphi Seattle, utilizando as bibliotecas RestClient que se não me engano vieram a partir do XE5.

e aqui a tela do WebService rodando :

Baixe os fontes do Rest WebService Application
Baixe o RestClient
Considerações sobre o ambiente
Concorrência
Não há restrições neste framework vindos do Componente idHttpServer visto que o mesmo utiliza os limites da máquina para executar as threads, então se sua máquina possui só um pouquinho de memória, pode ter certeza pelo menos umas 100 threads simultâneas serão executadas normalmente.
O que determinará o desempenho será o volume de dados retornado, a complexidade da query e e esforço que o banco faz para executá-la, quantidade de memória, CPU.
Aqueles que quiserem realizar testes de estabilidade e performance fiquem a vontade para fazer e divulgar resultados.
Testes de concorrência
O componente idHttpServer que foi utilizado aqui, implementa MultiThread, cada requisição vinda de um client qualquer, é criada uma thread para processamento. Para realização de testes de concorrência, retire o TStringList e a gravação em arquivo texto dos métodos e a substitua por banco de dados. A utilização desse artifício aqui foi somente para efeito de demonstração da aplicação, arquivos textos não funcionarão como persistência de dados numa aplicação multi-threaded a não ser que se implemente sincronização das threads o que também gera atraso na performance.
Tratamento de erros
Num aplicativo servidor não se permite a exibição de mensagens e nem deixar que solte exceções, todas elas devem ser tratadas e guardadas em log se for o caso, se você permitir o sistema soltar uma exceção por um erro qualquer por exemplo numa query, o webservice vai parar de responder e precisará ser reestartado.
Então faça sempre uso em todos os ServerMethods de Try/Except seguindo a forma abaixo :
Try
Try
// Instruções do Server Method aqui
Except
ON E:Exception Do
// Adiciona Log em arquivo texto
End
Finally
// Libera objetos se tiver algum
End;
Tipos Retornados
Os tipos retornados são sempre String em JSON padronizando ao máximo o que se espera de um servidor REST.
Atualização da Thread Principal
Os memos criados na tela principal (frmMain) foram usados apenas para teste e demonstração da aplicação, em produção retire a atualização desses memos ou faça a gravação deles em arquivo a cada mudança de data, limpando o memo logo em seguida, Pois um Memo que nunca é limpo irá consumir toda a sua memória.
Versões do Delphi compatíveis
O RestWebService foi feito no XE2, e devido a versão do Indy ser diferente em versões mais novas do Delphi, foi preciso colocar compilação condicional. Então para as versões XE2, XE7, XE8 e DX Seattle a compilação condicional já foi feita, basta abrir compilar e rodar.
Para que funcione nas versões XE6, XE5, XE4 e XE3 basta replicar a compilação condicional dessas versões e compilar, veja os pontos onde foi usada na Unit ServerUtils :

e na Unit ServerMethodsUnit1 :

Conclusão
Penso que o objetivo inicial foi atingido, que era ter um webservice REST + JSON utilizando apenas o Indy e o Delphi professional.
O DataSnap é um produto completo, bem pensado, cheio de recursos e CARO ! é um produto para quem quer ter uma vasta gama de opções e funcionalidades a seu dispor.
Para aplicações menores em que os recursos existentes no Datasnap não são tão essenciais, uma aplicação servidora como essa pode resolver e você economizar uma boa grana usando este mini-framework na versão Professional do Delphi pagando apenas 1/3 do valor da versão Enterprise, a que vem com o DataSnap.
E aí gostou ? se gostou, comente !
Veja aqui o próximo artigo : autenticação no servidor REST