Comunicação Local no Silverlight

by Israel Aece 23. August 2010 13:29

Como sabemos, podemos consumir serviços WCF em aplicações Silverlight. Isso nos permite colocar alguma tarefa em um serviço na aplicação que faz a hospedagem do serviço ou até mesmo em outros locais, fora do domínio qual ela se encontra. A ideia aqui é permitir a comunicação entre a aplicação Silverlight (que roda dentro do navegador) e serviços que estão hospedados além dessa máquina.

Mas e se precisarmos de algo mais simples, como por exemplo, a comunicação entre duas aplicações Silverlight que rodam dentro do navegador em uma mesma máquina? Utilizar o WCF aqui pode ser extremamente complicado, pois exigirá desenvolvermos uma série de funcionalidades para permitir esse interação, e ainda podemos ter um grande overhead, já que a comunicação não será local e dependerá de que a conexão esteja disponível com o servidor/serviço com qual queremos dialogar.

Para esse tipo de cenário o Silverlight fornece algo bem mais simples de se trabalhar, que é uma funcionalidade conhecida como "comunicação local". A ideia aqui é criar aplicações que geram informações, e aquelas que estiverem interessadas, podem capturá-las e tratar da forma que achar mais conveniente. Esse tipo de comunicação pode ser utilizada por plugins que rodam dentro da mesma página, em páginas diferentes, estando ou não dentro do mesmo domínio.

Através das imagens abaixo podemos analisar os cenários mais comuns. Na primeira imagem (1) temos uma única página ASPX hospedando duas aplicações Silverlight, e se precisarmos efetuar a comunicação entre elas, podemos utilizar o recurso que veremos aqui. Já na segunda imagem (2), temos duas página distintas dentro de um mesmo domínio, mas cada página hospeda uma aplicação Silverlight diferente, e da mesma forma, a comunicação também será possível. Na terceira (3) e última imagem, temos duas aplicações, mas cada uma delas está hospedada em um domínio diferente, mas isso também pode ser permitido, mas falaremos mais disso abaixo.

A implementação é relativamente simples. Para utilizarmos a comunicação local entre aplicações Silverlight, vamos recorrer aos tipos que estão disponível a partir do namespace System.Windows.Messaging. As principais classes que teremos aqui são: LocalMessageSender e LocalMenssageReceiver. Como podemos perceber, a primeira classe é responsável por enviar mensagens, enquanto a segunda se encarrega de receber tais mensagens.

Aqui é importante comentarmos sobre algumas "limitações": tudo o que podemos mandar de um lado à outro deve ser, obrigatoriamente, uma string. Qualquer coisa diferente disso, exigirá um trabalho extra, como serializar o objeto em algum formato Xml ou Json. Dependendo da quantidade de informação que você levar de um lado para outro, considere o uso do Json ao invés do Xml, que é bem menos verboso, e isso pode evitar você atingir o limite máximo do tamanho da string, que deve ser menor ou igual a 40KB. Uma outra alternativa aqui é serializar as informações em formato binário e utilizar Base64 para codificá-la.

Se vamos gerar uma aplicação que gera informações, então temos que recorrer ao uso da classe LocalMessageSender. Em seu construtor ela recebe uma string que corresponde ao nome do destinatário das mensagens geradas por ela. Já o segundo parâmetro do construtor desta classe, recebe uma string contendo o domínio que receberá as mensagens. Você pode utilizar o membro estático chamado Global desta mesma classe, que corresponde à um "*", e instrui o Silverlight que qualquer domínio poderá receber as mensagens. Finalmente, essa mesma classe fornece o método chamado SendAsync, que permite o envio de uma string para aqueles que desejarem capturá-la.

Chega o momento de efetuar a configuração do destinatário. Agora entra em cena a classe LocalMessageReceiver, que recebe em seu construtor três parâmetros: uma string com o nome do destinatário, uma das opções do enumerador ReceiverNameScope e, finalmente, uma coleção de strings, que representa os domínios de quais a aplicação poderá receber mensagens. Essa classe fornece um evento chamado MessageReceived, que é disparado quando uma nova mensagem chega para a aplicação, e para capturá-la, tudo o que precisamos fazer é nos vincularmos a este evento e acessar a mensagem através da propriedade Message do argumento MessageReceivedEventArgs. Para finalizar, o método Listen é responsável por começar a monitorar as mensagens que chegam e entregá-las para o evento acima.

Depois de conhecer essas classes, podemos já fazer o uso delas nas nossas aplicações. Para exemplificar, teremos duas aplicações Silverlight, hospedadas em locais diferentes. A ideia será permitir a comunicação entre elas, e para manter a simplicidade do exemplo, tudo o que a aplicação fará ao receber a mensagem, será colocá-la em um TextBox. Abaixo temos a configuração de uma das aplicações, utilizando tudo o que vimos acima:

public partial class MainPage : UserControl
{
    private LocalMessageSender sender;
    private LocalMessageReceiver receiver;

    public MainPage(string aplicacaoLocal, string aplicacaoRemota)
    {
        InitializeComponent();

        this.sender = new LocalMessageSender(aplicacaoRemota, LocalMessageSender.Global);
        this.receiver = new LocalMessageReceiver(
            aplicacaoLocal, ReceiverNameScope.Global, LocalMessageReceiver.AnyDomain);

        this.receiver.MessageReceived += 
            new EventHandler<MessageReceivedEventArgs>(receiver_MessageReceived);
        this.receiver.Listen();
    }

    private void receiver_MessageReceived(object sender, MessageReceivedEventArgs e)
    {
        this.Mensagens.Text += string.Format("{0}{1}", e.Message, Environment.NewLine);
    }

    private void EnviaMensagem_Click(object sender, RoutedEventArgs e)
    {
        this.sender.SendAsync(this.Mensagem.Text);
    }
}

O código da outra aplicação será exatamente igual. O único detalhe ali é o construtor da classe MainPage, que recebe como parâmetros o nome local e o nome remoto. O nome remoto é utilizado no construtor da classe LocalMessageSender, pois quero enviar mensagens para este destino. Já o nome local é utilizado pelo LocalMessageReceiver, que é de onde estou interessado em receber mensagens. Mais detalhes sobre estes parâmetros na próxima seção do artigo.

Com isso em funcionamento, ao rodar as aplicações já podemos perceber o resultado. Através da imagem abaixo, vemos que o conteúdo do TextBox da aplicação 01 foi para a aplicação 02 e vice-versa.

Parametrizando as Aplicações

Acima pudemos perceber que a classe que corresponde ao controle do Silverlight (MainPage), foi parametrizada com as duas informações, que correspondem de onde a aplicação quer receber e para onde ela quer enviar informações. Esses nomes não precisam, necessariamente, corresponder a nome de aplicações. A ideia é ter um nome coerente com o tipo de informação que está sendo gerada.

Outro detalhe importante é com relação ao nome que você define para o recebedor das mensagens, pois podemos ter a mesma aplicação carregada em outra instância do navegador, e ao definir o mesmo nome, uma exceção do tipo ListenFailedException será disparada para informar que já existe um recebedor registrado com este mesmo nome.

Para evitar isso, utilizaremos a "ponte" que existe entre o Silverlight e o código HTML que o hospeda. Isso permitirá gerarmos um valor randômico via Javascript e definir na inicialização do plugin, que será encaminhada para aqueles parâmetros que vimos no construtor da classe MainPage. Para que isso funcione, temos que adicionar um novo elemento <param /> na tag object que hospeda a aplicação Silverlight (*.xap), e nomear este parâmetro como initParams. O código abaixo ilustra a adição deste novo parâmetro, com algumas informações suprimidas por questões de espaço.

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2">
    <param name="source" value="ClientBin/Aplicacao01.xap"/>
    <param
        name="initParams"
        value="AplicacaoLocal=Aplicacao01,AplicacaoRemota=Aplicacao02" />
    <!-- Outros Parâmetros -->
</object>

A configuração refere-se à aplicação 01. Para a aplicação 02, esses parâmetros estão invertidos, já que terão papéis diferentes.

Com isso, o valor colocado ali será entregue em formato de um dicionário ao evento Startup da classe Application, que estará acessível através do arquivo App.xaml. É neste momento que extraímos os parâmetros deste dicionário e preenchemos o construtor da classe MainPage, assim como podemos notar através do código abaixo:

private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = 
        new MainPage(e.InitParams["AplicacaoLocal"], e.InitParams["AplicacaoRemota"]);
}

Conclusão: Neste artigo vimos a utilização de uma técnica interessante que nos permite a comuicação local (no mesmo computador) de aplicações Silverlight. Podemos utilizar esse recurso para cada vez mais criar aplicações independentes, e se mais tarde, precisarmos de interação entre elas, podemos recorrer à este recurso simples, mas que traz um grande poder as mesmas.

ComunicaoLocalSL.zip (144.66 kb)

Tags:

CSD

Acessando serviços de forma relativa

by Israel Aece 20. July 2010 22:36

Como sabemos, aplicações Silverlight podem recorrer a recursos que rodam no servidor que a hospeda utilizando WCF. Com isso, a aplicação que serve como host para o serviço é uma aplicação ASP.NET tradicional (WebForms ou MVC, Web Site ou Web Application), onde efetivamente criaremos o serviço WCF para que servirá a aplicação Silverlight.

Uma vez com o serviço pronto, tudo o que precisamos fazer para consumir o serviço no Silverlight, é a referência para o mesmo, utilizando a opção Add Service Reference, fornecida pela IDE do Visual Studio .NET. Como é feito em qualquer aplicação cliente, o Visual Studio extrairá as informações do documento WSDL e criará o proxy do lado do cliente, incluindo um arquivo chamado ServiceReferences.ClientConfig, que conterá as configurações de acesso ao serviço referenciado. O código abaixo ilustra esse arquivo:

<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IContrato"
                 maxBufferSize="2147483647"
                 maxReceivedMessageSize="2147483647">
          <security mode="None" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:1551/ServicoDeTeste.svc"
                binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_IContrato"
                contract="Servico.IContrato"
                name="BasicHttpBinding_IContrato" />
    </client>
  </system.serviceModel>
</configuration>

Como podemos notar, endereço do serviço WCF está definido de forma absoluta. Isso implica em alguns problemas que podemos ter durante a distribuição/instalação do projeto no servidor. Esse arquivo é comprimido para dentro de um outro arquivo, com extensão *.xap, e que por sua vez, é depositado no diretório ClientBin do projeto ASP.NET que hospeda a aplicação Silverlight. Ao colocar isso no servidor onde ele deverá rodar, provavelmente o endereço do serviço mudará, e ficará complicado e trabalhoso para alterar.

Para melhorar isso, a versão 4.0 do Silverlight permite especificar, de forma relativa, o endereço para o serviço, ou seja, não precisamos mais definir o endereço completo até o arquivo *.svc. Apesar da IDE ainda não ser inteligente o bastante para utilizar esse recurso ao referenciar o serviço, nada nos impede de alterar isso diretamente no arquivo de configuração do lado do cliente, apontando relativamente para o serviço WCF. Para o teste, eu criei um serviço na raiz da aplicação ASP.NET, e como a aplicação Silverlight é colocada na pasta ClientBin desta mesma aplicação, tudo o que precisamos fazer é subir um nível (../). Abaixo podemos visualizar essa ligeira mudança, mas que traz uma grande flexibilidade.

<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IContrato"
                 maxBufferSize="2147483647"
                 maxReceivedMessageSize="2147483647">
          <security mode="None" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="../ServicoDeTeste.svc"
                binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_IContrato"
                contract="Servico.IContrato"
                name="BasicHttpBinding_IContrato" />
    </client>
  </system.serviceModel>
</configuration>

Tags: , ,

WCF

Silverlight e a chamada assíncrona de serviços

by Israel Aece 22. June 2010 23:47

Em geral, podemos consumir os serviços WCF de duas formas: síncrona ou assíncrona. Na primeira opção, ao referenciar o serviço em uma aplicação cliente e invocar um das operações que ele disponibiliza, a comunicação será realizada e enquanto ela não retorna, a aplicação ficará bloqueada aguardando o resultado. Já no segundo modelo, ao realizar a chamada de forma assíncrona, a operação será realizada em um thread secundária, permitindo que a aplicação continue trabalhando enquanto o serviço é executado.

Como sabemos, o Silverlight tem cada vez mais espaço como sendo front-end de aplicações. Essa tecnologia recorre à serviços quando precisa buscar algum conteúdo remoto, como por exemplo, preencher os dados em um controle ListBox quando um botão for pressionado. Atualmente, a maioria desses serviços são construídos em WCF, e referenciados na aplicação Silverlight para permitir que a mesma interaja com o servidor para extrair os dados necessários para executar o seu trabalho.

Para aqueles que já utilizam essa técnica, já devem ter percebido que no Silverlight, só podemos consumir esses serviços de forma assíncrona, ou seja, recorrendo à uma segunda thread através do par de métodos BeginXXX/EndXXX ou através de eventos. Mas porque isso acontece ou porque é necessário?

O Silverlight possui apenas uma única thread, que é chamada de UI Thread, e como sabemos, operações de I/O bound, como é o caso da comunicação através da rede, são tarefas custosas e que podem levar um grande tempo para ser executada, e justamente por isso, se o Silverlight bloqueasse a thread de UI enquanto executa essa requisição, o host (que é o navegador), também seria bloqueado. Mesmo que você tente emular uma chamada síncrona, utilizando algum recurso primitivo de sincronização (como o ManualResetEvent), você teria problemas do mesmo jeito, já que quando invocar o método WaitOne desta classe, ele bloqueará a thread de UI para esperar o resultado, que nunca chegará, pois o resultado somente será entregue para o Silverlight quando a thread de UI não estiver executando nenhum código, que não é o caso aqui, e como já era de se esperar, teremos um deadlock.

Desde o proxy de serviços WCF até classes de baixo nível, como é o caso da WebClient, terão o mesmo comportamento, ou seja, deverão ser sempre acionadas através do modelo assíncrono. Tudo isso se deve ao fato de que os navegadores atuais implementam a NPAPI (Netscape Plugin Application Programming Interface). A NPAPI trata-se de uma API multi-plataforma desenvolvida pela Netscape que permite que plugins sejam utilizados dentro dos navegadores. Para que os plugins sejam considerados multi-plataforma, eles precisam seguir rigorosamente essa API, o que determina que métodos remotos sejam executados assincronamente, e como já percebemos, o Silverlight segue o que foi definido por ela.

Tags: ,

Async | WCF

UserName e Password em serviços WCF para Silverlight

by Israel Aece 10. January 2010 13:19

Como sabemos, podemos utilizar usuário e senha para autenticar um usuário em um serviço WCF, mas há algumas regras que precisam ser seguidas, como eu já comentei neste post. Uma dessas regras é que não podemos trafegar essas informações sem alguma criptografia. Mesmo que o serviço seja exposto via HTTP, a utilização de um certificado (não do HTTPS), fará este papel.

Eu mostrei como customizar a autenticação neste artigo, e note que estou usando um certificado e o serviço não está disponível através do HTTPS. Além disso, o binding que está sendo utilizado é o WSHttpBinding, que sabe como efetuar a criptografia das informações. Agora imagine que este serviço pode ser consumido por uma aplicação Silverlight. Infelizmente o WCF do Silverlight não tem suporte ao WSHttpBinding, somente ao BasicHttpBinding, e mais recentemente, via NetTcpBinding.

Mas como podemos proceder neste caso? Para poder disponibilizar serviços WCF para uma aplicação Silverlight, precisaremos recorrer a segurança em nível de transporte, para garantir a proteção. Para isso, estamos obrigados à expor o serviço através de HTTPS, e as credenciais estarão protegidas dentro da mensagem. Mas antes da versão 3.0 do Silverlight, era díficil expor e/ou consumir serviços WCF que exigem usuário e senha, já que você precisava criar headers customizados para acomodar essas informações, assim como era (ou ainda é) feito pelos ASP.NET Web Services.

A partir da versão 3.0, o Microsoft introduziu mais uma opção no binding BasicHttpBinding (do Silverlight), que é a TransportWithMessageCredential. Com esta opção definida, o serviço deverá ser disponibilizado via HTTPS, e as credencias viajarão dentro da mensagem, sem precisarmos escrever qualquer código adicional. Felizmente, quando efetuamos a referência em uma aplicação Silverlight, o proxy gerado já disponibiliza a propriedade ClientCredentials, onde podemos atribuir o usuário e senha, que eventualmente vem de controles TextBoxes da tela.

A configuração do serviço é relativamente simples. O primeiro passo é criar o validador customizado de usuários, que nada mais é do que uma classe que herda de UserNamePasswordValidator, e dentro do método Validate, você escreve a regra para buscar e validar o usuário. Para maiores detalhes de como isso funciona, consulte este artigo. Depois note também que definimos a segurança como sendo TransportWithMessageCredential, conforme comentamos acima.

<system.serviceModel>
  <services>
    <service name="ServicoDeClientes"
             behaviorConfiguration="bhr">
      <endpoint name="srv"
                binding="basicHttpBinding"
                contract="IContrato"
                bindingConfiguration="bc"/>
    </service>
  </services>
  <bindings>
    <basicHttpBinding>
      <binding name="bc">
        <security mode="TransportWithMessageCredential"/>
      </binding>
    </basicHttpBinding>
  </bindings>
  <behaviors>
    <serviceBehaviors>
      <behavior name="bhr">
        <serviceMetadata httpsGetEnabled="true"/>
        <serviceCredentials>
          <userNameAuthentication
            customUserNamePasswordValidatorType="ValidadorDeUsuarios"
            userNamePasswordValidationMode="Custom"/>
        </serviceCredentials>
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

Depois da configuração do serviço, o publicamos e, consequentemente, referenciamos na aplicação cliente (Silverlight). Ao referenciá-lo, já podemos notar que o arquivo de configuração já está devidamente especificado com a mesma opção de segurança: TransportWithMessageCredential.

<configuration>
  <system.serviceModel>
    <client>
      <endpoint address="https://localhost/SLTest/ServicoDeClientes.svc"
                binding="basicHttpBinding"
                bindingConfiguration="srv"
                contract="ServicoDeClientes.IContrato"
                name="srv" />
    </client>
    <bindings>
      <basicHttpBinding>
        <binding name="srv">
          <security mode="TransportWithMessageCredential" />
        </binding>
      </basicHttpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

Com isso, tudo o que nos resta é instanciar o proxy e definir a propriedade ClientCredentials, que receberá os credenciais do usuário. Depois de configurado, você já está apto a chamar qualquer operação exposta pelo serviço, que antes dela ser efetivamente executada, o runtime do WCF avaliará as credenciais informadas, se certfificando de que são válidas, e somente a partir daí, irá invocar o método.

ServicoDeClientes.ContratoClient p = new ServicoDeClientes.ContratoClient();
p.ClientCredentials.UserName.UserName = this.Login.Text;
p.ClientCredentials.UserName.Password = this.Password.Text;

p.PingCompleted += (s, args) => this.textBlock1.Text = args.Result;
p.PingAsync();

A chamada da operação de forma assíncrona, nada tem nada a ver com a questão de segurança. Isso é apenas uma característica do WCF do Silverlight, e mesmo que não houvesse qualquer nível de segurança envolvido, a forma para invocar uma operação seria a mesma.

Tags: , ,

Security | WCF

Serviços TCP no Silverlight

by Israel Aece 20. November 2009 21:07

A cada dia que o passa, o Silverlight tem ganhado cada vez mais espaço nos sites que são construídos ao redor do mundo. Motivo para a Microsoft evoluir essa tecnologia rapidamente, adicionando cada vez mais funcionalidades dentro dele.

Os adeptos ao Silverlight não se limitam à aqueles construtores de sites institucionais, que na maioria das vezes, são criados para campanhas publicitárias e com poucas funcionalidades dinâmicas. Ele também está atingindo as empresas desenvolvem os softwares para uso interno, justamente porque a Microsoft incorpora nele, funcionalidades que permitem o uso em aplicações do tipo LOB (Line of Business).

Uma das novidades mais recentes, que afeta principalmente o uso de aplicações Silverlight em intranets, é que a partir da versão 4.0, teremos a possibilidade de referenciar e consumir serviços construídos em WCF, através do protocolo TCP (net.tcp).

Como disse no parágrafo acima, esse tipo de protocolo é útil em intranets, já que não haverá restrições de eventuais firewalls, e mesmo que tenha, tudo é negociável. É importante dizer, que as aplicações Silverlight estão condicionadas à utilizarem o seguinte intervalo de portas: 4502 à 4534, ou seja, somente conseguirá acessar um serviço WCF exposto através de uma delas.

Uma das principais características do protocolo TCP, é que possibilita a comunicação duplex, ou seja, permite que o serviço se comunique com o cliente, através de callbacks. Isso já era possível no Silverlight, através do Polling Duplex, que nada mais é do que uma especialização do protocolo BasicHttpBinding, mas que simula callbacks em cima do protocolo HTTP.

Outra grande vantagem do protocolo TCP quando comparado com o HTTP, é a performance, e mesmo em aplicações Silverlight, essa vantagem é bastante significativa, e um dos grande responsáveis por isso, é a codificação binária. Neste momento, para usar este protocolo, você deve abrir mão da segurança em nível de transporte, que não é suportado.

Os tipos necessários para acessar um serviço WCF através de TCP, estão contidos no assembly System.ServiceModel.NetTcp.dll, localizado em %Program Files%\Microsoft SDKs\Silverlight\v4.0\Libraries\Client. Mas isso não quer dizer que você terá uma classe chamada NetTcpBinding, assim como há no .NET Full. O Silverlight utiliza um CustomBinding, composto com os seguintes bindings elements: BinaryMessageEncodingBindingElement e TcpTransportBindingElement. Ao referenciar o serviço TCP em uma aplicação Silverlight 4.0, a IDE do Visual Studio 2010 já faz as seguintes entradas no respectivo arquivo de configuração:

<configuration>
    <system.serviceModel>
        <bindings>
            <customBinding>
                <binding name="NetTcpBinding_IContrato">
                    <binaryMessageEncoding />
                    <tcpTransport
                        maxReceivedMessageSize="2147483647"
                        maxBufferSize="2147483647" />
                </binding>
            </customBinding>
        </bindings>
        <client>
            <endpoint
                address="net.tcp://localhost:4502/srv"
                binding="customBinding"
                bindingConfiguration="NetTcpBinding_IContrato"
                contract="Servico.IContrato"
                name="NetTcpBinding_IContrato" />
        </client>
    </system.serviceModel>
</configuration>

Ao contrário de outros tipos de comunicação e de aplicações, não basta simplesmente termos o cliente e o serviço funcionando para que eles possam se comunicar. Mesmo através de TCP, este protocolo também está condicionado às políticas de cross-domain.

Para comunicação HTTP, tudo o que precisamos é definir um arquivo XML na aplicação, que permite o acesso. Para suportar a comunicação através de TCP, o Silverlight resolve a questão da restrição de cross-domain de uma forma mais rebuscada. A Microsoft criou uma template de projeto chamada Silverlight TCP Socket Policy, que pode ser acessada a partir das templates Online do Visual Studio 2010 ou através do Visual Studio Gallery, neste endereço.

Essa template dá origem à um projeto do tipo Console, que expõe o arquivo de cross-domain (definido no arquivo SocketPolicy.cs), definindo o intervalo de portas que é permitido que aplicações Silverlight utilizem. Ao rodar as aplicações, certifique-se também de que este projeto esteja rodando, caso contrário, você receberá uma exceção do tipo CommunicationException, informando que você não tem permissões para efetuar o acesso ao serviço, sugerindo que talvez esteja faltando o arquivo de cross-domain.

Conclusão: Apesar de ainda estar em sua versão Beta, o Silverlight 4.0 traz uma série de melhorias e novas funcionalidades, que facilitará cada vez mais a criação de aplicações LOB, e com certeza, uma das principais limitações até então, era o consumo de serviços WCF através do protocolo TCP, que é fator determinante em aplicações deste tipo.

Tags: , , ,

WCF

Propagando exceções para o Silverlight

by Israel Aece 23. September 2009 09:56

O WCF é uma das formas que temos para estabelecer a comunicação entre uma aplicação Silverlight e a aplicação (ASP.NET) que a hospeda. Com o WCF podemos permitir que o Silverlight se conecte e envie e/ou receba informações, pois como sabemos, ele roda dentro do navegador do usuário. Apesar de você não ter acesso a maior parte dos recursos do WCF, o básico você consegue fazer.

Antes da versão 3 do Silverlight, há uma grande dificuldade em propagar as exceções que esse serviço, eventualmente, disparasse. Qualquer exceção que ocorra dentro do WCF, a resposta é enviada ao cliente definindo o código de resposta do HTTP (Http Status Code) como 500, o que indica um erro interno do servidor e impede o navegador de ler qualquer resposta. Esse problema pode ser facilmente contornado, criando um message inspector, verificando antes de enviar a resposta para o cliente, se a mensagem é ou não uma fault. Caso seja, você altera o status do HTTP para 200 (OK) ao invés de 500. O código abaixo ilustra como criar esse behavior customizado:

public class SLFaultMessageInspector : IDispatchMessageInspector
{
    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        if (reply.IsFault)
            reply.Properties[HttpResponseMessageProperty.Name] =
                new HttpResponseMessageProperty() { StatusCode = HttpStatusCode.OK };
    }

    //....
}

Isso ainda poderia ser feito na versão 2 do Silverlight, mas o cliente não conseguia interpretar a fault, ou melhor, traduzí-la para uma exceção, e permitir que o desenvolvedor trate o erro da forma tradicional. Já a versão 3, incorpora a capacidade ao Silverlight de processar as faults, podendo especificar os contratos de faults ou optar por não especificá-los, para assim propagar ao cliente os objetos que representam e trazem maiores informações sobre o problema que ocorreu do outro lado.

Tags: , , ,

WCF

ASP.NET Session em serviços WCF

by Israel Aece 22. September 2009 10:46

Como foi visto aqui, podemos utilizar as variáveis de sessão do ASP.NET para manter o estado de um serviço WCF. Para isso, basta habilitar o modo de compatibilidade com ASP.NET e você já terá acesso ao HttpContext (HttpContext.Current) em seu serviço.

Você precisa ter um certo cuidado aqui com dois detalhes: o primeiro deles é com relação a dependência que o seu serviço terá com o protocolo HTTP, pois dentro da classe que representa o serviço, você mencionará classes que fazem parte do ASP.NET, assim como a HttpContext, e com isso, você não conseguirá acessá-lo através de TCP. Já o segundo detalhe, é com relação ao consumo deste serviço. Ao contrário do gerenciamento de instâncias do WCF, a sessão do ASP.NET é utilizada de forma diferente, mas com a mesma finalidade, ou seja, de manter o estado das informações durante as requisições.

Quando você faz uso do modelo PerSession, ao fechar o proxy o WCF descartará a instância da classe do lado do serviço. Como no caso do ASP.NET é ele quem controla as variáveis de sessão, você precisará explicitamente removê-las. Para que você consiga descartar tudo o que tem na memória, precisará chamar o método Abandon da classe HttpSessionState. Para automatizar essa tarefa e não poluir a Interface do contrato com métodos que "limpam" a sessão, você pode implementar na classe do serviço a Interface IDisposable, que será invocada quando o proxy for encerrado.

Como sabemos, a configuração padrão da sessão, é manter um cookie do lado do cliente com o "Id", para que ela consiga identificar o usuário. Para que isso funcione corretamente, precisamos habilitar o gerenciamento de cookies pelo binding, assim como é mostrado neste artigo. Com isso, o proxy do WCF encaminhará, automaticamente, os cookies nas futuras requisições. Mesmo que você utilize instâncias diferentes do proxy, devido ao recurso de caching, elas compartilharão as mesmas variáveis de sessão do lado do serviço.

Você pode consumir um serviço WCF na mesma aplicação em que ele está hospedado (comum em aplicações Silverlight). Mesmo que você utilize o mesmo diretório virtual/worker process, você não conseguirá dentro do serviço acessar as variáveis de sessão criadas no ASP.NET e vice-versa. Lembre-se de que o serviço nem sempre será consumido por essa mesma aplicação, e justamente por isso, é proibido. Se você precisar passar informações que estão armazenadas em variáveis de sessão do lado do cliente, tudo o que você precisa fazer, é enviá-las através do contrato.

Tags: , , ,

ASP.NET | WCF

Persistência de dados em aplicações Silverlight

by Israel Aece 8. July 2009 08:47

Grande parte das aplicações Web, independentemente de qual tecnologia ela foi criada, tem a necessidade de manter informações por um determinado tempo. Essas informações, na maioria das vezes, refletem alguma configuração definida pelo cliente, que customiza alguma seção do aplicativo, e quer manter isso em algum local para não precisar refazê-la mais tarde.

Quando estamos falando em uma aplicação desenvolvida em ASP.NET, você utiliza algumas funcionalidades definidas pela tecnologia, tais como Session, Caching, Profile, ViewState ou Cookies, para atingir esse objetivo. A escolha entre qual utilizar, vai depender do escopo, performance e nível de segurança. Esses detalhes devem ser cuidadosamente analisados, tentando equilibrar cada um dos pontos mencionados.

Aplicações construídas em Silverlight também tem a mesma necessidade, precisando, de alguma forma, armazenar informações para serem utilizadas posteriormente. Como sabemos, o Silverlight possui uma versão do .NET Framework exclusiva, trazendo várias APIs para o gerenciamento de estado. Como o aplicativo desenvolvido em Silverlight está rodando no navegador, você não tem acesso à variáveis de sessão, Caching ou Profile, já que estão acessíveis apenas pelo ASP.NET e do lado do servidor. Devido a isso, a Microsoft disponibilizou no Silverlight o Isolated Storage. Esse recurso não é exclusidade do Silverlight, e já está disponível no .NET Framework desde a sua primeira versão, e foi reescrito para o Silverlight, ganhando alguns novos conceitos.

Com o Isolated Storage podemos montar uma estrutura de arquivos para a aplicação, armazenando qualquer tipo de informação, desde um simples arquivo texto até um arquivo mais complexo (como a serialização de um objeto em formato binário/Xml). Do ponto de vista do desenvolvedor, temos uma estrutura para armazenamento das informações bem próximo ao que é o sistema de arquivos. O Isolated Storage é como os cookies (armazenar do lado do cliente), mas ao invés de armazenar somente chave/valor (strings), é um sistema de arquivos virtual, dando muito mais flexibilidade e segurança.

Apesar de uma aplicação Silverlight também ser acessada e executada através de um navegador, ela diferente bastante de uma aplicação Web tradicional. Ao contrário dessas aplicações tradicionais, que são executadas no servidor e apenas o HTML correspondente é enviado ao cliente, aplicações Silverlight são consideradas parcialmente confiáveis, e rodam dentro do navegador em um ambiente conhecido como “sandboxing”, que protegem o computador do cliente de aplicações maliciosas, evitando que aplicações Silverlight acessem recursos da máquina, compromentendo-a.

Antes de trabalharmos com o Isolated Storage, precisamos conhecer como funciona o nível de isolamento que ele fornece. O isolamento permitirá definirmos o escopo de acesso de uma determinada informação. O Silverlight possui dois níveis de isolamento: usuário + aplicação ou usuário + site. O primeiro deles irá isolar as informações pelo usuário dentro da aplicação (XAP), estando acessíveis somente a partir da mesma. Já a segunda opção, irá isolar as informações pelo usuário dentro do site, e todas as aplicações Silverlight que estão acessíveis a partir do mesmo site (domínio), irão compartilhar a informação de acordo com o usuário. É importante ficar claro que cada usuário tem dois locais de armazenamento, sendo um deles exclusivo para uma determinada aplicação, enquanto o outro pode ser acessado de qualquer aplicação, desde que esteja dentro daquele mesmo site.

O local de armazenamento e leitura dos arquivos estão condicionados ao usuário que está acessando a aplicação. Isso quer dizer que o Silverlight somente poderá acessar (para leitura ou gravação) o diretório local, que estará localizado debaixo do perfil do usuário atual. Esse caminho poderá variar de acordo com o sistema operacional, onde no Windows Vista está em C:\Users\<Usuario>\AppData\LocaLow\Microsoft\Silverlight\is e no Windows XP em C:\Docuemnts and Settings\<Usuario>\Local Settings\Application Data\Microsoft\Silverlight\is. É importante dizer que você não precisa se preocupar com o endereço, pois ao utilizar o Isolated Storage, ele se encarregará de salvar ou ler as informações do local correto, não permitindo que uma aplicação maliciosa acesse, de forma arbitrária, o sistema de arquivos da máquina cliente, comprometendo a segurança.

Depois da parte teórica, vamos analisar a API que temos disponível para manipular o Isolated Storage. Basicamente temos quatro classes a nossa disposição, estando todas elas debaixo do namespace System.IO.IsolatedStorage, mas separadas em dois assemblies diferentes, sendo o mscorlib.dll e o System.Windows.dll.

A primeira classe que temos é a IsolatedStorageFile. A finalidade desta classe é representar uma das areas disponíveis no Isolated Storage para um determinado usuário, abstraindo o sistema de arquivos virtual que ele nos fornece. Essa classe não pode ser instanciada diretamente; ao invés disso, ela possui dois métodos estáticos chamados GetUserStoreForApplication e GetUserStoreForSite, quais retornam instâncias da mesma, com as respectivas áreas de armazenamento. A partir da instância desta classe, podemos criar, excluir ou enumerar diretórios e arquivos. Para efetuar essas tarefas, essa classe fornece métodos autoexplicativos, tais como: CreateDirectory, DeleteDirectory, GetFilesNames, GetDirectoryNames, DeleteFile e CreateFile.

Os métodos para recuperar arquivos ou diretórios pode, opcionalmente, receber uma string, representando um critério de busca, especificando um subdiretório, ou até mesmo utilizar o caracter “*”, para refinar ainda mais a busca. O método CreateFile cria o arquivo desejado e retorna uma instância da classe IsolatedStorageFileStream, representando o arquivo recém-criado, para que você consiga gravar ou ler as informações do mesmo. O exemplo abaixo ilustra como proceder para criar e ler um arquivo dentro do repositório de uma aplicação específica:

//Criando o Arquivo
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
    using (IsolatedStorageFileStream stream = store.CreateFile(“Teste.txt”))
        using (StreamWriter sw = new StreamWriter(stream))
            sw.WriteLine(“Teste”);

//Lendo o Arquivo
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
    using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(“Teste.txt”, FileMode.Open, store))
        using (StreamReader sr = new StreamReader(stream))
            MessageBox.Show(sr.ReadToEnd());

Observação: Os exemplos acima não estão contemplando o tratamento de erros. Todas as operações que se faz com o Isolated Storage, você deve envolver em um bloco try/catch, monitorando por possíveis exceções do tipo IsolatedStorageException. Um cenário onde ela pode ser lançada, é quando o usuário desabilita o acesso ao Isolated Storage ou atinge a cota máxima.

Cotas

Imagine que uma determinada aplicação possui um código malicioso, que cria arquivos sem nenhum controle. Em breve, não haverá mais espaço disponível no disco do cliente, que foi todo tomado pela aplicação. Felizmente o Isolated Storage disponibiliza o conceito de cotas. As cotas são um recurso bastante interessante, garantindo que a quantidade de informações geradas pela aplicação não ultrapasse um limite imposto pelo Silverlight. O Silverlight utiliza o conceito de grupos para gerenciar as cotas. Com isso, aplicações oriundas de um mesmo site, compartilham a mesma cota.

Por padrão temos 1MB de espaço disponível para cada grupo. Com uma aplicação Silverlight rodando no navegador, ao clicar com o botão direito, você tem a opção Silverlight Configuration. Ao abrir a janela, haverá uma aba chamada Application Storage. Lá temos a relação dos sites que estão fazendo uso deste recurso, exibindo o tamanho atual e o máximo permitido. Essa tela de configuração ainda permite você gerenciar o Isolated Storage, como excluí-los ou até mesmo desabilitá-lo. A imagem abaixo exibe essas configurações:

A partir da instância da classe IsolatedStorageFile temos acesso à alguns membros, que permitem manipular as cotas. O primeiro deles é a propriedade AvailableFreeSpace, que como o próprio nome diz, retorna a quantidade de espaço disponível para aquela “store”. Ainda temos a propriedade Quota, que representa a quantidade máxima permitida para aquela “store”.

A idéia do Isolated Storage é armazenar poucas informações, e justamente por isso, que 1MB é um valor razoável. Mas e se precisarmos de um tamanho maior para armazenar as informações que estão sendo geradas pelas aplicações? É neste momento que entra em cena o método IncreateQuotaTo. Este método recebe como parâmetro a nova cota, retornando um valor boleano indicando se a alteração foi ou não realizada com sucesso. Isso se deve ao fato de que somente podemos aumentar a capacidade de armazenamento do Isolated Storage, com o consentimento do usuário. Ao chamar esse método, uma mensagem aparece na aplicação (como mostrado na figura abaixo), informando o usuário se ele permite ou não esse crescimento.



O código abaixo ilustra como podemos proceder para efetuar o aumento da capacidade do Isolated Storage para 10MB, utilizando os membros que vimos acima:

int space = 1024 * 1024 * 10;

using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
    if (store.AvailableFreeSpace < space)
        if (store.IncreaseQuotaTo(space))
            MessageBox.Show("Cota aumentada com sucesso.");

Persistindo Objetos

Em algumas situações podemos querer armazenar instâncias de classes que a aplicação manipula, para mais tarde recuperá-las e dar prosseguimento ao que usuário estava fazendo, evitando que ele refaça essas tarefas. Para atingir esse objetivo, basta serializarmos o objeto em formato binário ou Xml com os formatadores que já conhecemos, em conjunto com o Stream gerado pelo Isolated Storage.

Como isso será uma tarefa comum, a Microsoft facilitou a vida dos desenvolvedores, criando uma classe chamada de IsolatedStorageSettings (a única que está contida no Assembly System.Windows.dll). Essa classe nada mais é do que um dicionário de dados, onde a chave é do tipo string e o valor do tipo object, ou seja, podemos armazenar a instância de qualquer objeto do .NET (desde que ele seja serializável).

Essa classe não pode ser instanciada diretamente pela aplicação. Ao invés disso, você captura as instâncias predefinidas a partir de duas propriedades estáticas que ela mesma fornece: ApplicationSettings e SiteSettings. Escolher qual delas usar, irá depender do nível de compartilhamento das informações que você desejar, assim como já vimos acima.

Para manipular esse dicionário, podemos utilizar os conhecidos métodos Add e o indexer, que dado um chave retorna o respectivo valor. Se informar uma chave inexistente para o indexer, uma exceção será disparada. Para evitar isso, essa mesma classe fornece um método chamado TryGetValue, que dado a chave e um parâmetro de saída representando o valor correspondente, retorna um valor boleano indicando se foi ou não possível recuperar o valor. Esse procedimento é mostrado através do código a seguir:

IsolatedStorageSettings store = IsolatedStorageSettings.ApplicationSettings;
store.Add("Cliente", new Cliente() { Email = "ia@israelaece.com", Nome = "Israel Aece" });

Cliente cliente = null;

if (store.TryGetValue<Cliente>("Cliente", out cliente))
    MessageBox.Show(cliente.Nome);

Conclusão: Vimos no decorrer deste artigo os benefícios e facilidades que temos ao trabalhar com ele, mas devemos levar em consideração que essas informações ficam armazenadas no disco, e alguém pode encontrar os arquivos gerados e manipulá-los, comprometendo a aplicação. Justamente por isso, nunca armazene informações sensíveis dentro dele, ou de qualquer arquivo no disco, a menos que esteja devidamente protegido.

PersistenciaDeDadosEmSL.zip (63.76 kb)

Tags: ,

.NET Framework

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Host