multipleSiteBindingsEnabled

Há algum tempo eu comentei aqui sobre uma dificuldade que existe quando hospedamos serviços WCF no IIS. Neste mesmo post, eu mostrei a solução que foi incorporada na versão 3.5 do WCF.

Para facilitar isso, a partir da versão 4.0 do WCF temos uma opção chamada multipleSiteBindingsEnabled, disponível através do elemento serviceHostingEnvironment, que quando definido como True, permite que o serviço fique disponível a partir dos múltiplos endereços de acesso configurados no IIS. Abaixo temos o arquivo de configuração com esta opção habilitada:

<system.serviceModel>
    <serviceHostingEnvironment multipleSiteBindingsEnabled=”true”/>
</system.serviceModel>

Consumindo serviços WCF com jQuery

O jQuery é uma biblioteca com várias funcionalidades que tornam o desenvolvimento de código JavaScript muito mais simples. Tarefas que demandavam várias linhas de código para chegar a um determinado resultado, atualmente com o jQuery o código necessário para atingir esse mesmo objetivo é muito menos complexo. Inclusive a Microsoft reconheceu a facilidade e a larga adoção pelos desenvolvedores, e decidiu incorporá-lo às templates de projeto do ASP.NET.

Entre as mais variadas funcionalidades que o jQuery oferece, uma delas é o suporte ao AJAX, que nos permite invocar métodos que estão do lado do servidor. Mas além disso, uma das possibilidades que temos é a requisição para serviços, incluindo serviços construídos com WCF. A finalidade deste artigo é mostrar como proceder para criar serviços WCF que possam ser consumidos pelo AJAX, e depois disso, vamos analisar o que o jQuery oferece para o consumo do mesmo.

Primeiramente vamos nos atentar a construção de um serviço para que possamos consumí-lo através do AJAX/jQuery. O que precisamos nos atentar é que para expor esse serviço para clientes AJAX, é necessário decorarmos as operações não somente com o atributo OperationContractAttribute, mas também com os atributos WebGetAttribute ou WebInvokeAttribute, que são disponibilizados a partir da versão 3.5 do .NET Framework, e que estão contidos debaixo do namespace System.ServiceModel.Web (assembly System.ServiceModel.Web.dll).

Como exemplo, o contrato irá manipular instância da classe Usuario, que nada mais é do que um objeto simples, que possui três propriedades autoexplicativas: Codigo, Nome e Email. O contrato fornecerá três operações: RecuperarUsuario, que retorna a instância da classe Usuario devidamente configurada com os parâmetros de entrada, uma outra operação chamada RecuperarUsuarios que retorna uma coleção de usuários e, finalmente, a operação Adicionar, que dado a instância do usuário, irá adicioná-lo em algum repositório. Abaixo podemos visualizar a interface que servirá como o contrato para o serviço:

[ServiceContract]
public interface IUsuarios
{
    [OperationContract]
    [WebInvoke(
        BodyStyle = WebMessageBodyStyle.Wrapped,
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json)]
    Usuario RecuperarUsuario(string nome, string email);

    [OperationContract]
    [WebGet(
        BodyStyle = WebMessageBodyStyle.Bare,
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json)]
    Usuario[] RecuperarUsuarios();

    [OperationContract]
    [WebInvoke(
        BodyStyle = WebMessageBodyStyle.Bare,
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json)]
    void Adicionar(Usuario usuario);
}

Note que as operações que serão expostas também determinam algumas configurações específicas para quando forem consumidas a partir do AJAX. É importante perceber também que em todas elas, estamos optando por trabalhar com JSON ao invés do XML para formular a mensagem de requisição e de resposta, já que o JSON é mais simples de se trabalhar com Javascript. A propriedade BodyStyle controla como o corpo da requisição/resposta deve ser formatado, determinando se os parâmetros ou o resultado dos métodos devem ou não estar dentro de elementos adicionais. Para maiores detalhes sobre esses parâmetros, consulte este artigo.

Ao interceptar a requisição/resposta que é feita para a primeira operação, então teremos as informações em JSON sendo trafegadas, e as duas pontas (WCF e o jQuery) se encarregam de fazer toda a tradução necessária, aliviando assim nosso trabalho. Antes de analisarmos o código necessário para consumir serviços WCF no jQuery, vamos analisar o conteúdo que está sendo trafegado para as mensagens (requisição e resposta respectivamente) do contrato que criamos acima:

[ Operação RecuperarUsuario ]

POST http://localhost/Services/ServicoDeUsuarios.svc/RecuperarUsuario HTTP/1.1
Content-Type: application/json
— OUTROS PARAMETROS OMITIDOS —

{ “nome”: “Israel Aece”, “email”: “ia@israelaece.com” }

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
— OUTROS PARAMETROS OMITIDOS —

{“RecuperarUsuarioResult”:{“Codigo”:123,”Email”:”ia@israelaece.com”,”Nome”:”Israel Aece”}}

[ Operação RecuperarUsuarios ]

GET http://localhost/Services/ServicoDeUsuarios.svc/RecuperarUsuarios HTTP/1.1
— OUTROS PARAMETROS OMITIDOS —

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
— OUTROS PARAMETROS OMITIDOS —

[{“Codigo”:1,”Email”:”usuario1@servidor.com.br”,”Nome”:”Nome do Usuario 1″},{“Codigo”:2,”Email”:”usuario2@servidor.com.br”,”Nome”:”Nome do Usuario 2″},{“Codigo”:3,”Email”:”usuario3@servidor.com.br”,”Nome”:”Nome do Usuario 3″},{“Codigo”:4,”Email”:”usuario4@servidor.com.br”,”Nome”:”Nome do Usuario 4″}]

[ Operação Adicionar ]

POST http://localhost/Services/ServicoDeUsuarios.svc/Adicionar HTTP/1.1
Content-Type: application/json
— OUTROS PARAMETROS OMITIDOS —

{“Codigo”:123,”Nome”:”Israel”,”Email”:”ia@israelaece.com”}

Note que a URL de requisição também contempla o método a ser disparado do lado do serviço. Os parâmetros de entrada são serializados em JSON, e o resultado idem. Na operação RecuperarUsuario, configuramos ela para que o resultado seja Wrapped, e com isso, o corpo da mensagem de retorno veio envolvida em um objeto com o nome de RecuperarUsuarioResult, ao contrário daquelas definidas como Bare, onde a requisição/resposta não estão envolvidas neste elemento adicional.

Além dos detalhes que são necessários em nível de contrato, precisamos nos atentar em como hospedar esse serviço. Você precisa configurar o endpoint do WCF para utilizar um binding exclusivo para este cenário, que é o WebHttpBinding. Se você estiver hospedando no IIS (como é o caso do exemplo acima), então provavelmente haverá um arquivo com extensão *.svc que representa o serviço. É dentro deste arquivo que precisamos trocar o “fábrica” de hosts, que deve apontar para o WebServiceHostFactory, assim como já mostrei neste artigo.

Depois de todo o serviço WCF já devidamente criado e rodando, precisamos entender a API do jQuery para consumir esse serviço de exemplo. Nos dois tipos de projetos ASP.NET (WebForms e MVC), ambos já trazem o JQuery referenciado, o que nos permite acessar o que ele fornece para consumir serviços, escritos em qualquer tecnologia. Sendo assim, o primeiro passo importante para acessar qualquer um dos recursos fornecidos pelo jQuery, devemos adicionar uma referência para o arquivo *.js correspondente ao mesmo, e para isso, podemos utilizar o seguinte elemento:

http://Scripts/jquery-1.4.1.js

Depois deste arquivo referenciado, então já podemos utilizar a API do jQuery para consumir serviços WCF. Vamos utilizar neste exemplo a função $.ajax, que é uma função de baixo nível do jQuery, e que nos permite customizar, através de uma série de parâmetros, as configurações para controlar como a mensagem será enviada e como a resposta será processada.

Entre os vários parâmetros, temos o type, que determina com a requisição será enviada ao serviço, podendo ser através de GET ou POST. A propriedade url determina o endereço até o serviço, incluindo o nome da operação a ser invocada. contentType é onde definimos como o corpo da mensagem está formatada, e apesar do jQuery utilizar um tipo genérico, que pode ser aplicado em grande parte dos casos, é sempre melhor deixar isso explícito com o tipo que você está operando. A propriedade data determina o que vai ser enviado para a operação. Essa propriedade trabalha em conjunto com uma outra propriedade chamada processData. Quando você define os dados a serem enviados para o serviço através do método GET, então os dados serão convertidos em query strings, e a propriedade processData previne esse comportamento.

Além dessas propriedades, temos algumas opções para a configuração de callbacks para alguns cenários. beforeSend permite efetuar algum processamento momentos antes da requisição ser enviada ao serviço. A opção success permite especificarmos um callback apontando para um método que deverá ser disparado quando o resultado voltar com sucesso. Já error também permite especificarmos um método a ser disparado quando algum problema acontecer no serviço. É importante dizer aqui que o tratamento de erro não será tão simples, pois o WCF não traduz automaticamente a exceção em um erro no formato JSON, o que dificulta para mostrar a mensagem de erro. Finalmente, temos um callback chamado de complete, que como o próprio nome diz, é disparado quando a requisição é finalizada, independemtente se ocorreu ou não alguma exceção durante o seu processamento.

Abaixo temos como podemos configurar a função $.ajax para executarmos a primeira operação fornecida pelo serviço WCF que criamos anteriormente, que é a RecuperarUsuario. Como o acesso deve ser feito através do método POST, então os parâmetros serão enviados no corpo da mensagem, em formato JSON. Do lado do serviço, o WCF consegue interpretar o objeto JSON que foi enviado, extrair cada propriedade e preencher cada um dos parâmetros exigidos pela operação.

function RecuperarUsuario() {
    $.ajax(
    {
        type: “POST”,
        url: “http://localhost/Services/ServicoDeUsuarios.svc/RecuperarUsuario&#8221;,
        contentType: “application/json”,
        data: ‘{ “nome”: “Israel”, “email”: “ia@israelaece.com” }’,
        processData: false,
        success:
            function (resultado) {
                alert(resultado.RecuperarUsuarioResult.Nome);
                alert(resultado.RecuperarUsuarioResult.Email);
            },
        error:
            function (xhr, textStatus, errorThrown) {
                alert(‘Algum Problema Ocorreu!’);
            }
        });
    }

É importante perceber que configuramos (no contrato) o corpo da mensagem para o método RecuperarUsuario como Wrapped, e que isso nos obrigará a acessar o resultado através do objeto que envolve as propriedades do usuário, e que é criada automaticamente pelo JSON, que neste caso é chamada de RecuperarUsuarioResult.

Já a segunda operação exposta pelo serviço WCF, que retorna uma coleção de usuários, vamos acessá-la através do método GET, e ao receber o resultado, vamos iterar pela coleção, acessando elemento a elemento mostrando as propriedades Nome e Email de cada usuário retornado pelo serviço. É importante perceber aqui que estamos acessando diretamente as propriedades, sem passar por aquele elemento que é gerado pelo JSON. Isso se deve ao fato de termos configurado a respectiva operação como Bare, que evita envolver o resultado neste membro extra.

function RecuperarUsuarios() {
    $.ajax(
    {
        type: “GET”,
        url: “http://localhost/Services/ServicoDeUsuarios.svc/RecuperarUsuarios&#8221;,
        success:
            function (usuarios) {
                $.each(usuarios, function (indice, usuario) {
                    alert(usuario.Nome + “: ” + usuario.Email);
                })
            }
        });
    }

Finalmente temos o método AdicionarUsuario, que recebe como parâmetro a instância da classe Usuario e o adiciona em algum repositório. O usuário é criado utilizando a sintaxe JSON, onde configuramos cada propriedade e seu respectivo valor através de uma espécie de dicionário. Só que o jQuery não traz nativamente funções para representar o objeto em formato de string, algo que é necessário para enviá-lo até o serviço. Ao invés de fazer todo o trabalho árduo para essa transformação, podemos recorrer à uma biblioteca fornecida através do site oficial do JSON, chamada de JSON2.js. Essa biblioteca fornece dois métodos para a manipulação do JSON, sendo eles: parse e stringify. O método parse retorna o objeto devidamente reconstruído a partir de uma estrutura JSON, enquanto o método stringify retorna uma string contendo representação JSON de um determinado objeto. É justamente o resultado deste segundo método que estamos enviando ao serviço:

function AdicionarUsuario() {
    var usuario = { “Codigo”: 123, “Nome”: “Israel”, “Email”: “ia@israelaece.com” };

    $.ajax(
    {
        type: “POST”,
        url: “http://localhost/Services/ServicoDeUsuarios.svc/Adicionar&#8221;,
        contentType: “application/json”,
        data: JSON.stringify(usuario),
        processData: false,
        success:
            function (resultado) {
                alert(‘Usuário adicionado com sucesso.’);
            }
        });
    }

Apenas atente-se que para este código funcionar, precisamos fazer o download do arquivo json2.js e referenciá-lo na página, como vemos abaixo:

http://Scripts/json2.js

Conclusão: Consumir serviços a partir de AJAX pode tornar a experiência do usuário muito melhor, já que evita a necessidade de ter efetuar a atualização completa da página. Isso já era uma necessidade, mas o jQuery torna isso muito mais simples, onde mesmo utilizando funções de baixo nível como vimos aqui, a tarefa acaba sendo muito mais simples de se realizar.