Nos dias de hoje vivemos em um mundo cada vez mais conectado (virtualmente falando), o que nos faz criar sites e aplicações que consumam ou forneçam informações uns aos outros, tendo um conteúdo mais consistente, reutilizável e, principalmente, sincronizado. Sendo assim, existem diversas fontes de informação ao redor do mundo, que provê os mais variados conteúdos que podemos consumir em nossas aplicações.
Só que trazendo isso para o lado técnico, nos deparamos com uma grande limitação (por questões de segurança), que é o consumo de informações por parte de uma aplicação Web, onde ela somente está autorizada a consumir dados que são disponibilizados a partir do mesmo domínio de onde ela se originou (Same Origin Policy).
Uma das alternativas que temos atualmente para burlar isso, é a utilização do JSONP. Com este recurso, podemos efetuar requisições GET para um determinado recurso, mesmo que esteja além do domínio de origem, e conseguiremos recuperar as informações desejadas. O problema do JSONP é que ele somente trabalha com o verbo GET, ou seja, outros verbos não são suportados, nem mesmo o POST, já que a requisição JSONP faz uso da tag <script />, levando os parâmetros somente via querystring.
Mais cedo ou mais tarde vamos nos deparar com a necessidade de submeter algo além de simples GETs para um servidor. Para resolvermos isso, existe uma solução que está lentamente se tornando uma saída viável, é o CORS (Cross-Origin Resource Sharing). Basicamente, o CORS é uma especificação que está sendo criada e regida pelo W3C, qual define uma forma de acesso à recursos que estão em diferentes domínios.
O CORS é implementado no objeto XmlHttpRequest, e a ideia por trás dele é o uso de headers customizados que são injetados na requisição e resposta do HTTP, e que faz com que o cliente (navegador) e o servidor se conheçam, afim de determinar se a requisição deve ou não ser executada com sucesso, sendo algo mais ou menos como acontece com o Client Access Policy do Silverlight.
Observação: Apesar do XmlHttpRequest ser comum para todos os navegadores, infelizmente no caso do Internet Explorer, esse recurso é implementado em um objeto separado, chamado de XDomainRequest. Com isso, ele nos obriga a criar um código diferenciado caso estejamos rodando o código no navegador da Microsoft. A novidade é que o time do IE, recentemente publicou em seu blog, que o CORS está sendo implementado diretamente no objeto XmlHttpRequest, ficando compatível com a grande maioria dos navegadores do mercado.
Ao invés de utilizar diretamente estes objetos, podemos recorrer ao jQuery, que graças à um recurso chamado de detecção de funcionalidades, ele é capaz de detectar a presença dos objetos mencionados acima, e se existirem, podem fazer uso dele. No caso do CORS não é diferente, e vamos utilizá-lo aqui para mostrar como podemos proceder para consumir um recurso que está em outro domínio. Para o exemplo, considere o diagrama abaixo:
Como mencionei acima, é através da verificação dos headers na requisição/resposta, que o navegador decide se pode ou não concluir o processo. Se interceptarmos a requisição que é realizada, podemos notar as informações que são trocadas, incluindo os “novos” headers. Abaixo temos a requisição e a resposta, mas omitindo algumas informação irrelevantes para a explicação:
[ Requisição ]
GET http://localhost:5342/Servicos/ViaGet?valorDeEnvio=abc HTTP/1.1
Host: localhost:5342
Origin: http://localhost:5560
Referer: http://localhost:5560/Index.htm
[ Resposta ]
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:5560
Content-Type: application/json; charset=utf-8
{“ValorInformado”:”abc”}
Note que a requisição é realizada através da aplicação que está localizada no endereço http://localhost:5560, encaminhando-a para um serviço que encontra-se em um outro local, além desta, localizada no endereço http://localhost:5342. O grande detalhe a se notar na requisição é o header Origin, que é controlado pelo navegador, que o injeta na requisição. Já na resposta, o servidor analisa se aquele site solicitante (de qual partiu a requisição) possui ou não permissão para executar a requisição. Para indicar ao navegador que é permitido o acesso, basta retornar o endereço da aplicação que solicitou (ou * para qualquer uma) como valor do header Access-Control-Allow-Origin. Caso não informe nenhuma destas duas informações, a requisição não será concluída.
Requisições via GET com JSONP podem ser encaminhadas para outros servidores sem problemas; podemos também postar (POST) um formulário para um outro domínio. Em ambos os casos, não haveria necessidade de utilizar CORS. Qualquer outra requisição que diferencie destes critérios, deve utilizar o CORS para atingir o objetivo que queremos, e também exigirá uma cooperação por parte do servidor para conseguir executa-la.
Por exemplo, métodos como PUT ou DELETE, ou ainda, o envio de alguns headers ou de headers customizados, também podem ser controlados pelo servidor, pois será ele que definirá quais headers e/ou verbos que poderão ser acessíveis aos clientes. Para exemplificar, vamos supor que desejamos enviar um header customizado para o serviço, e ele deverá nos dizer se ele pode ou não ser encaminhado. Vamos analisar a imagem abaixo para ilustrar o comportamento deste outro cenário:
Note que agora temos duas requisições sendo realizadas ao servidor. A primeira é conhecida como preflight request, qual utiliza o método OPTIONS do HTTP, que tem a finalidade de extrair alguma informação a respeito da comunicação, e como já suspeitamos, vai trazer as informações necessárias para o navegador determinar se pode ou não ser executada. Vamos analisar a as requisições:
A primeira requisição encaminha ao servidor (1), além do Origin, mais dois headers: Access-Control-Request-Method e Access-Control-Request-Headers. O primeiro deles questiona o servidor se o cliente pode ou não executar um GET, enquanto o segundo questiona se o cliente pode ou não encaminhar os headers ali definidos. Na resposta para a requisição de OPTIONS, mais dois – novos – headers são devolvidos (2): Access-Control-Allow-Methods e Access-Control-Allow-Headers. O primeiro informa uma lista de métodos que são permitidos aquele cliente encaminhar; já o segundo, lista quais são os headers que podem ser encaminhados ao servidor. Depois de validado pelo navegador, a requisição efetiva é encaminhada para o servidor (3), qual retornará o conteúdo propriamento dito (4).
Como vimos, em duas etapas conseguimos realizar a comunicação entre domínios diferentes, com a possibilidade de refinar como ela deve ser executada. Como mencionei anteriormente, isso parece ser uma opção viável as técnicas que temos hoje para a comunicação entre domínios, e pelo que parece, há um grande empenho por parte dos fabricantes a adotá-lo. E, para finalizar, ainda é necessário que o servidor (serviço), saiba lidar com este tipo de requisição, que será tema para um outro post.