Uma das grandes necessidades no consumo de serviços por parte das aplicações Web, é consumir serviços criados e que estão além da própria aplicação cliente, que provavelmente estará debaixo de um outro domínio, e por questões de segurança, isso não será permitido diretamente, já que se utilizado de forma maliciosa e sem o consentimento do usuário, pode por em risco as informações e a navegação dos usuários.
Para contornar essa “limitação”, então precisamos de alguma técnica para possibilitar que essa tarefa seja realizada. Uma opção seria a criação de um serviço nesta aplicação cliente, que serviria como um wrapper, e este por sua vez, consumiria diretamente o serviço, sem as imposições de chamada entre domínios que o navegador impõe. Com isso, o código JavaScript irá consumir este serviço local, que por sua vez, encaminharia a requisição para o serviço remoto, em outro domínio.
Apesar desta técnica funcionar, ela acaba sendo uma solução ruim, já que teremos que envolver outros elementos para realizar uma tarefa relativamente simples. Felizmente o jQuery fornece nativamente o suporte a uma técnica conhecida como JSONP (JSON with Padding).
O seu funcionamento não é muito complicado. Como disse acima, requisições entre domínios não são permitidas, mas há uma única exceção: a tag , ou seja, podemos definir no elemento src (source) a URL para um recurso que está além do nosso domínio, que o navegador não irá proibir o acesso. O que o JSONP faz é justamente o uso dela, criando dinamicamente esta tag, e definindo no atributo src a URL do serviço que estamos tentando acessar.
Na verdade isso não funciona sozinho, ou seja, precisa de uma certa colaboração por parte do serviço, para que o cliente possa processar o resultado da forma correta. Além dos parâmetros que são exigidos pelo método do serviço, o jQuery inclui um parâmetro chamado callback, que é o nome de uma função criada temporariamente no cliente. Aqui entra em cena a infraestrutura do serviço, que deve ser capaz de retornar o resultado (dados) envolvido nesta função, e quando o mesmo chegar ao cliente, ele invocará para capturar o resultado e encaminhá-lo para o nosso código, e assim iremos manipular da forma que acharmos mais conveniente. Falaremos da implementação por parte do serviço mais tarde.
Para fazer tudo isso funcionar, precisamos nos atentar à alguns detalhes do lado do cliente e do lado do serviço. Do lado do cliente, tudo o que precisamos fazer é indicar para a API de AJAX do jQuery que ela deve utilizar JSONP. Para isso, devemos recorrer ao atributo dataType, que indica o tipo de dado/formato que você está esperando que o servidor te retorne. No nosso caso, vamos apontar jsonp, assim como é mostrado no código abaixo:
$(document).ready(function () {
$.ajax(
{
type: ‘GET’,
url: ‘http://ipv4.fiddler:1191/cotacoes’,
dataType: ‘jsonp’,
success: function (cotacoes) {
var itens = [];
$.each(cotacoes, function (indice, cotacao) {
itens.push(‘
‘);
});
$(‘
}
});
});
</head>
<body>
</body>
</html>
Por parte do cliente, o jQuery fornece parâmetros para realizarmos a configuração via JSONP. Como podemos perceber no código acima, em negrito temos do tipo da requisição sendo definido como JSONP, o que faz com que a requisição seja formatada da seguinte forma (repare o que temos em negrito):
GET http://127.0.0.1:1191/cotacoes?callback=jQuery15109440240194135102_1328569080908&_=1328569080915 HTTP/1.1
Accept: application/javascript, */*;q=0.8
Referer: http://ipv4.fiddler:1185/Index.htm
Accept-Language: en-US,pt-BR;q=0.5
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:1191
Connection: Keep-Alive
Como disse acima, é necessário que o serviço também de suporte à esta funcionalidade. A versão atual do WCF Web API, que está na 0.6, já traz esse recurso nativamente, e com poucas (talvez nenhuma) configurações, podemos construir e expor o serviço para que ele seja consumido através de um cliente que esteja hospedado em um outro domínio. Para o exemplo, temos um serviço “dummy” de cotação de câmbio, que possui os dados estáticos. Apenas como observação, não é mais necessário que o serviço seja decorado com o atributo ServiceContractAttribute.
public class ServicoDeCotacoes
{
[WebGet]
public IEnumerable<Cotacao> RecuperarCotacoes()
{
return new[]
{
new Cotacao() { Moeda = “Dólar Americano”, ValorEmReal = 1.7263M },
new Cotacao() { Moeda = “Euro”, ValorEmReal = 2.2653M },
new Cotacao() { Moeda = “Libra”, ValorEmReal = 2.7308M }
};
}
}
Finalmente, tudo o que precisamos fazer com o serviço é configurá-lo através da classe WebApiConfiguration, algo como já era realizado nas versões anteriores desta API. Abaixo temos um código simples, que ilustra como procedemos para realizar tal configuração:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
ConfigurarServico();
}
private static void ConfigurarServico()
{
RouteTable.Routes.MapServiceRoute<ServicoDeCotacoes>(
“cotacoes”,
new WebApiConfiguration()
{
CreateInstance = (t, ic, msg) => new ServicoDeCotacoes()
});
}
}
Ao executar a requisição para o serviço e interceptando-a, teremos na resposta da mesma, a função criada dinamicamente no cliente que será executada automaticamente pelo próprio jQuery. Mais uma vez, repare o que temos negritado abaixo. Note que o nome da função que foi criado pelo jQuery e repassado para o serviço, é devolvido para o mesmo.
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 06 Feb 2012 22:58:00 GMT
X-AspNet-Version: 4.0.30319
jsonp-callback: jQuery15109440240194135102_1328569080908
Content-Length: 170
Cache-Control: private
Content-Type: application/javascript; charset=utf-8
Connection: Close
jQuery15109440240194135102_1328569080908([{“Moeda”:”Dólar Americano”,”ValorEmReal”:1.7263},{“Moeda”:”Euro”,”ValorEmReal”:2.2653},{“Moeda”:”Libra”,”ValorEmReal”:2.7308}])
Nenhuma configuração extra foi realizada por parte do serviço, porque a Microsoft entende que isso seja uma necessidade comum, o que faz com que já venha devidamente configurado. Internamente temos um operation response handler chamado JsonpHttpResponseHandler, que é responsável por avaliar se a requisição é ou não uma requisição do tipo JSONP, e para isso, verifica se há uma querystring chamada callback. Caso queira alterar o nome desta querystring, pode-se recorrer à propriedade CallbackQueryParameter deste próprio handler.
Além disso, ainda temos um media type customizado, chamado de JsonpMediaTypeFormatter, que envolve o resultado gerado pelo serviço, pela função (callback) criada pelo jQuery, que entenderá isso como o resultado que será repassado para a função de sucesso, configurada na chamada do método $.ajax.