Requests Ajax com Cross-Origin Resource Sharing (CORS) entre Sencha Touch e ExtJS e Backend
Oi pessoal,
Muitas pessoas às vezes perguntam como uma aplicação que está em um dominio1.com fazer requests Ajax para o dominio2.com ou até mesmo subdomínios do mesmo domínio.
A resposta que o Ext JS e Sencha Touch tem para isso é usar o proxy JsonP, onde você faz um request cross-domain e consegue ler um JSON como resposta. Nesse caso um script javascript será adicionado automaticamente na página para que essa leitura seja possível. Mas o JsonP fazer apenas requests GET, ou seja, só é possível ler dados.
Com uma requisição GET também é possível enviar parâmetros na url, mas como você vai fazer login passando usuário e senha na própria URL (http://dominio1.com?user=loiane&senha=123456)? Não dá né gente!?
Será que é possível então fazer requests AJAX usando GET, POST ou até mesmo REST (GET, POST, PUT, DELETE) para domínios diferentes (cross-domain)?
É possível sim e a resposta é o CORS (Cross-Origin Resource Sharing).
Nesse caso, quando a gente usa CORS precisamos mudar apenas o backend para que este aceite requisições cross-origin. No Ext JS ou Sencha Touch não precisamos fazer nada, vamos usar um proxy Ajax ou Rest (o que preferir) normalmente.
Para adicionar o CORS no backend é muito simples. Nessa página tem todos os detalhes: http://enable-cors.org/
No PHP por exemplo, basta adicionar o seguinte código no início do código de cada arquivo PHP que irá aceitar requisições cross-origin:
<?php header('Access-Control-Allow-Origin: *'); ?>
Se você usa Java ou Python, pode olhar os seguintes links:
- Java: http://software.dzhuvinov.com/cors-filter.html (esse filtro é ótimo, uso bastante!)
- Python: https://github.com/monsur/cors-python
- Mais infos: http://enable-cors.org/resources.html
É importante saber também que nem todos os browsers suportam CORS: http://enable-cors.org/client.html
Ok, pode nos mostrar um exemplo Loiane pra ficar mais claro?
Claro que sim! :)
Vou usar o esse exemplo como base, que é um CRUD que já usei algumas vezes aqui no blog.
Se você executar locamente vai rodar tranquilo.
Agora, vou retirar a parte PHP e vou fazer deploy em outro servidor/computador e vou executar o Ext JS de um computador. Para melhor exemplificar, vou executar o código Ext JS no código do meu laptop e o PHP vai ficar deployado no meu desktop:
Meu desktop - computador 01:
Meu notebook - computador 02:
Como os códigos cliente e servidor estão separados, na Store do ExtJS preciso atualizar a url, já que o acesso ao php não é mais local. No meu caso ficou assim:
api: { create: 'http://192.168.0.14/blog/sencha-cors-comp01/extjs4-crud-mvc/php/criaContato.php', read: 'http://192.168.0.14/blog/sencha-cors-comp01/extjs4-crud-mvc/php/listaContatos.php', update: 'http://192.168.0.14/blog/sencha-cors-comp01/extjs4-crud-mvc/php/atualizaContato.php', destroy: 'http://192.168.0.14/blog/sencha-cors-comp01/extjs4-crud-mvc/php/deletaContato.php' }
Se tentar executar agora, não vai funcionar e o Ext JS vai lançar uma exception dizendo que não consegue fazer esse request e/ou o servidor não consegue processar:
Note que o código onde está o código ExtJS é localhost (127.0.0.1) e estou tentando fazer request para 192.168.0.14, ou seja, domínios diferentes.
Agora vamos voltar no código PHP e adicionar o Access-Control-Allow-Origin: * - na verdade, no site do CORS fala que basta adicionar o header, mas isso não procede. Para que o seu código trate o CORS de maneira correta no PHP, é necessário um pouco mais que isso! Criei um arquivo novo chamado enableCORS.php com o seguinte conteúdo:
<?php function enableCORS() { // Allow from any origin if (isset($_SERVER['HTTP_ORIGIN'])) { header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}"); header('Access-Control-Allow-Credentials: true'); header('Access-Control-Max-Age: 86400'); // cache for 1 day } // Access-Control headers are received during OPTIONS requests if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) header("Access-Control-Allow-Methods: GET, POST, OPTIONS"); if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"); exit(0); } return true; } ?>
E no código para listar os contatos, por exemplo, o início do arquivo ficaria assim:
<?php include("enableCORS.php"); enableCORS(); //chama o arquivo de conexão com o bd include("connect.php"); $start = $_REQUEST['start']; $limit = $_REQUEST['limit']; $queryString = "SELECT * FROM contact LIMIT $start, $limit"; ...
E vamos tentar novamente:
Agora funciona! E basta fazer o mesmo para todos os arquivos PHP para que o CRUD funcione como esperado.
Se a gente olhar os detalhes do request, vamos ver as informações do response e ver que o request foi feito usando CORS:
O mesmo se aplica a alguma app Sencha Touch - ou até mesmo requisição Ajax com JQuery.
Caso queria conferir o código completo usado nesse post, segue o exemplo de CRUD com CORD habilitado: https://github.com/loiane/sencha-touch-extjs-CORS
Até a próxima! :)