ASP.NET Web API - Overview

by Israel Aece 22. February 2012 21:36

Como mencionei anteriormente, a partir da versão 4, o ASP.NET MVC passará a ter uma infraestrutura completa para a construção e hospedagem de serviços REST. Apesar de essencialmente ser baseado em uma template Web, há algumas mudanças em como implementar o serviço em relação a como fazíamos no WCF Web API.

Ao instalar essa versão, que ainda encontra-se em Beta, passamos a ter uma template de projeto chamada ASP.NET MVC 4, e ao selecioná-la, uma segunda tela é exibida para escolhermos uma entre as seis opções disponíveis, sendo que uma delas é a Web API. Basicamente, cada uma delas traz um projeto pré-configurado com os recursos necessários para que ele funcione corretamente.

Se começarmos a explorar esta template, já começamos a nos deparar com alguns destes recursos configurados. Em primeiro lugar, podemos notar a existência de novos assemblies, que são utilizamos para a construção de APIs Web. Entre eles, o principal é o System.Web.Http.dll, que possui grande parte dos tipos que são utilizados por estes serviços, incluindo tipos que serão utilizados diretamente pelo serviço, bem como tipos que são utilizados pela infraestrutura e pipeline.

Como estamos utilizando a infraestrutura do ASP.NET MVC, as classes que servirão como serviços, deverão herdar da classe abstrata ApiController, que possui características diferentes do Controller tradicional, próprio do MVC; e este novo controller fornece detalhes específicos para a construção de serviços. Os métodos que estarão acessíveis pelos clientes, serão métodos (ações) que estarão dentro desta classe, e que através do sistema de roteamento, deverá procurar e encontrar pela classe/método de acordo com a URI informada.

Dentro desta classe podemos receber e/ou retornar tipos simples, bem como tipos complexos, que são aquelas classes que criamos para representar nossas entidades. Se precisarmos de um acesso mais refinado, da requisição ou da resposta, podemos recorrer ao uso das classes HttpRequestMessage<T> e HttpResponseMessage<T>, mas que infelizmente na versão atual (ainda em Beta), não há nenhum model binder que faz conversão da requisição em um objeto do tipo HttpRequestMessage<T>, mas segundo o time, isso será resolvido em futuras versões. Se precisarmos acessar informações pertinentes à requisição, então devemos recorrer à propriedade Request diretamente, exporta pela classe ApiController. Abaixo temos um exemplo simples de como fica a classe que conterá os métodos de exemplo:

public class NoticiasController : ApiController
{
    private static IList<Noticia> noticias = new List<Noticia>();

    public IQueryable<Noticia> Get()
    {
        return noticias.AsQueryable();
    }

    public Noticia Post(Noticia noticia)
    {
        noticias.Add(noticia);
        return noticia;
    }
}

Com essa implementação, já é tudo o que precisamos para poder consumi-lo em uma aplicação cliente. Há um detalhe que passa despercebido no código acima, que é a nomenclatura dos métodos. Note que propositalmente nomeamos o primeiro como Get e o segundo como Post, que refletem dois dos mais comuns verbos do protocolo HTTP.

Na verdade, como parte da template do projeto, já há uma configuração no arquivo Global.asax, que determina uma rota padrão para os serviços. Copiei abaixo a configuração, e podemos notar que há espaço para o controller, mas nada é mencionado em relação ao método/ação na rota. Existem algumas deduções que ele faz, como por exemplo, verificar se dentro do controller há um método, sem parâmetros, onde o nome inicie com "Get...". O mesmo coisa acontece com o método Post e também para os outros métodos GET e PUT do HTTP.

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

Se desejar definir um nome diferente destas regras, você pode recorrer aos atributos HttpGetAttribute, HttpPostAttribute, HttpPutAttribute e HttpDeleteAttribute, para dizer ao framework o que e como pode ser acessado um determinado método dentro desta classe.

Algumas funcionalidades inerentes ao HTTP já estão devidamente configuradas, como por exemplo, basta apontar o header Content-Type ou Accept, do tipo do conteúdo ou tipo da resposta, respectivamente, e o ASP.NET Web API já sabe como deve ler/gerar o conteúdo. Além disso, já existe suporte nativo a alguns operadores do protocolo OData, como paginação, ordenação, etc., e para tudo isso funcionar, a única preocupação é que nosso método retorne a instância de uma classe que implemente a interface IQueryable<T>, e a partir daí, é só incorporar na querystring os parâmetros necessários.

Finalmente, podemos perceber neste pequeno trecho de código, que a construção de um serviço REST acaba ficando mais simples se comparado ao WCF Web API, e muito mais simples ainda, se compararmos com o WCF HTTP.

Tags: , ,

ASP.NET | CSD

WCF Web API agora é ASP.NET Web API

by Israel Aece 20. February 2012 08:39

Por algum tempo eu venho falando sobre um novo projeto da Microsoft chamado de WCF Web API, que trata-se de um espécie de estensão do WCF que facilita a construção e o consumo de serviços que são puramente Web (REST), ou seja, estão fortemente amarrados ao protocolo HTTP e o assumem como uma camada de aplicação. Abaixo temos alguns dos artigos que escrevi e que abordam essa tecnologia:

Apesar de possuir uma forma de hospedar esse tipo de serviço através de uma aplicação do tipo Windows (Windows Service, Console, etc.), a sua forma mais natural é utilizar o IIS, ou melhor, utilizar qualquer template de aplicação ASP.NET e lá criar a classe que representará o serviço, e apenas com uma linha de código, é o suficiente para mapear uma URI base para o serviço e tudo magicamente funciona, sem a necessidade de uma configuração extra no arquivo Web.config.

Foi a partir deste momento que nos perguntarmos porque não utilizar o próprio ASP.NET MVC? Com o ASP.NET você tem acesso a todo conjunto de recursos do HTTP, já que projetos Web tem afinidade com este protocolo. Além disso, a necessidade que os scripts tem em invocar um método C#, fez com que a Microsoft criasse um tipo de resultado chamado de JsonResult, qual retorna um valor "facilmente legível" para o Javascript.

Haviam certos overlaps entre o WCF Web API e o ASP.NET MVC, onde cada um deles determina uma forma específica de utilizar o mesmo protocolo, que é o HTTP. Então, para remover essa confusão, a Microsoft decidiu juntar essas duas tecnologias, e daí surgiu o ASP.NET Web API.

Com essa mesclagem alguns dos elementos que compunham o WCF Web API não existirão mais, justamente porque o ASP.NET MVC trata de outra forma. No ASP.NET Web API, agora vamos lidar com controllers e actions ao invés de uma classe decorada com o atributo ServiceContractAttribute e de métodos decorados com WebGetAttribute/WebInvokeAttribute. O roteamento do ASP.NET será utilizado para direcionar as requisições aos serviços. O model binding será o responsável por materializar o corpo da requisição em objetos complexos que, eventualmente, receberemos em nossos métodos. As actions filters serão utilizados ao invés de operation handlers, e ainda em tempo, se quisermos manipular complementamente a mensagem de requisição/resposta, continuamos tendo acesso às classes HttpRequestMessage e HttpResponseMessage.

Particularmente, eu gosto mais da ideia de ter um único framework que aborda os mais diferentes tipos de comunicação ao invés de atrelar isso especificamente a um tipo de projeto, mas também sou capaz de compreender a necessidade que a Microsoft tem de apartar isso, afinal, o WCF foi construído quando todos pensavam que o protocolo SOAP seria a solução para todo e qualquer tipo de comunicação existente, e o protocolo HTTP só seria mais um meio de transporte. Mas como percebemos ao longo do tempo, em certas ocasiões, ele é extremamente overkill. Mas é importante dizer que há funcionalidades que o SOAP possui que são bem interessantes, e se utilizadas da forma correta, podem trazer vários benefícios, mesmo em ambientes homogêneos. De qualquer forma, em posts futuros mostrarei como procedemos para construir serviços através desta nova tecnologia, que é o ASP.NET Web API.

Tags: , , ,

ASP.NET | CSD | WCF

Introdução ao CORS

by Israel Aece 16. February 2012 20:43

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.

Tags: ,

CSD

Habilitando JSONP no ASP.NET MVC

by Israel Aece 13. February 2012 12:24

Recentemente mostrei aqui como devemos proceder para fazer uso do JSONP no WCF Web API, que permite o consumo de serviços através de domínios diferentes. Algumas vezes, quando já existe um projeto Web do tipo ASP.NET MVC, podemos expor uma ação no controller para que ele seja consumido diretamente pelo lado cliente da mesma (Javascript/jQuery), sem qualquer problema referente à chamada cross-domain.

Mas se quisermos expor isso para clientes que estão além do mesmo domínio, vamos nos deparar com os problemas que já conhecemos e/ou vimos anteriormente. A classe JsonResult, que já é parte do MVC, expõe um conteúdo baseado em JSON, mas não é capaz de envolver a resposta em uma função de callback (JSONP), algo que é necessário para que o jQuery/JSONP possa ser capaz de processar o resultado.

Temos várias alternativas para que isso funcione corretamente, e uma delas, seria criar um resultado customizado para capturar o nome da função de callback, que no caso do jQuery, é encaminhada através da querystring chamada callback, e por fim, envolvemos o resultado definido no interior da ação do controller nesta função, retornando o resultado conforme é esperado pelo jQuery. Abaixo temos o código que define um resultado de uma requisição via JSONP:

public class JsonpResult : JsonResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        var callback = context.HttpContext.Request["callback"];

        if (string.IsNullOrWhiteSpace(callback))
            throw new ArgumentNullException("É necessário informar a função de callback");

        if (this.Data != null)
            context.HttpContext.Response.Write(
                string.Format("{0}({1});",
                    callback, 
                    new JavaScriptSerializer().Serialize(this.Data)));
    }
}

Com este novo tipo criado, podemos simplesmente retornar uma instância desta classe, definido a propriedade Data (herdada da classe JsonResult) com o valor do objeto que queremos serializar, e o JsonpResult se encarregará de fazer o restante do trabalho, deixando o controller isento de qualquer código que interessa - somente - à infraestrutura.

public class ServicosController : Controller
{
    public ActionResult ViaGet(string valor)
    {
        return new JsonpResult() { Data = new { ValorInformado = "bla bla bla" } };
    }
}

Tags: , ,

ASP.NET | CSD

Assincronismo no WPF

by Israel Aece 10. February 2012 12:31

Uma grande necessidade que existe ao escrevermos aplicações, é a necessidade de executar tarefas custosas e/ou periódicas (polling). Muitas vezes, se elas forem escritas em sua forma tradicional, vamos nos deparar com uma experiência ruim durante o uso do software, justamente porque essas tarefas serão executadas de forma síncrona, ou seja, até que elas não sejam concluídas, o usuário não poderá acessar qualquer outra área do sistema.

O .NET Framework fornece desde as primeiras versões suporte para escrita de código assíncrono. O problema é que a escrita deste código não é lá muito trivial, tendo que lidar com muitos detalhes de baixa nível, tais como asyncs results, callbacks, sincronização, etc. Para facilitar isso, a Microsoft trabalha na criação de novos recursos que serão incorporados na própria linguagem (C# e VB.NET), tornando a programação assíncrona muito mais simples de ser realizada, tornando-a tão intuitiva quanto a programação síncrona.

Além dessa mudança nas linguagens, ainda temos o Reactive Extensions, que uma das suas funcionalidades, é prover uma forma diferente de lidar com a programação assíncrona, que ao invés de "puxarmos" o resultado de algum lugar, podemos fazer com que esse resultado seja "empurrado" para a aplicação, o que lhe permitirá trabalhar de forma reativa. A finalidade deste artigo é apresentar como podemos proceder para trabalhar de forma assíncrona em uma aplicação Windows, e para o exemplo, vamos recorrer a um projeto baseado em WPF em conjunto com o Reactive Extensions.

Como disse anteriormente, podemos executar uma tarefa de forma assíncrona ou executar uma ação periodicamente. No primeiro caso, em que precisamos executar uma tarefa de forma assíncrona, podemos recorrer ao método de extensão ToAsync da classe Observable. Esse método possui centenas de overloads, qual contempla grande parte (senão todas) das versões dos delegates Action e Func. Sendo assim, podemos vincular diretamente um método que leva um tempo longo para executar, e com isso, reagir quando o resultado for retornado. A implementação deste código pode variar dependendo se você está ou não utilizando o padrão MVVM. Abaixo temos o exemplo de como podemos proceder para executar um cálculo custoso de forma assíncrona, utilizando Reactive Extensions, sem MVVM:

private void Operacao_Click(object sender, RoutedEventArgs e)
{
    int v1 = int.Parse(this.Valor1.Text);
    int v2 = int.Parse(this.Valor2.Text);

    Observable
        .ToAsync<int, int, int>(Somar)(v1, v2)
        .ObserveOnDispatcher()
        .Subscribe<int>(r => this.Resultado.Text = r.ToString());
}

private int Somar(int v1, int v2)
{
    Thread.Sleep(4000);

    return v1 + v2;
}

O único comentário relevante ao método Somar é que ele simula um processamento custoso através do método Sleep. Já no evento Click do botão, invocamos o método ToAsync, informando qual o método que deve ser disparado de forma assíncrona, incluindo logo na sequência, os parâmetros exigido pelo método Somar. Quando chamamos o método Subscribe, ele passa a monitorar qualquer resultado (de sucesso) gerado pelo método Somar, e neste caso, estamos apresentando-o em um terceiro TextBox.

Um detalhe extremamente importante é sobre o método ObserveOnDispatcher. O WPF possui uma classe chamada Dispatcher, que serve como um gerenciador de tarefas para serem executadas, e está sempre associada com uma determinada thread de UI. Isso quer dizer que qualquer notificação enviada pelo método Somar será enviada e executada pela própria thread que criou os controles de UI, já que aplicações Windows (WPF e Windows Forms) possuem essa afinidade. Se não nos atentarmos a este método, uma exceção do tipo InvalidOperationException será disparada, contendo a seguinte mensagem: The calling thread cannot access this object because a different thread owns it.

Já quando utilizamos MVVM, a implentação é um pouco diferente por conta da estrutura imposta pelo padrão. Os botões da View (Xaml) são executados através de um comando que deve implementar a interface ICommand. Neste caso, é muito comum recorrer à criação de um comando chamado de RelayCommand, que permite você injetar a lógica do comando a ser executado através de delegates. Só que é importante dizer que a execução deste comando é realizado de forma síncrona. Precisamos realizar uma pequena implementação para conseguir executar este mesmo comando de forma assíncrona. O método que representa a lógica a ser executada, pode ser executado de forma síncrona ou assíncrona, sem a necessidade de qualquer alteração no mesmo.

Abaixo temos o código que representa o comando que define que a execução de alguma tarefa seja realizada de forma assíncrona. Note que continuamos utilizando o Reactive Extensions, e no método ToAsync definimos o método que é informado no construtor desta mesma classe.

public class AsyncRelayCommand : RelayCommand
{
    public AsyncRelayCommand(Action execute)
        : base(execute) { }

    public AsyncRelayCommand(Action execute, Func<bool> canExecute)
        : base(execute, canExecute) { }

    public override void Execute(object parameter)
    {
        Observable
            .ToAsync(base.execute)()
            .Subscribe();
    }
}

Depois de criado esta classe, podemos fazer o uso da mesma diretamente em nossa ViewModel. Aqui optei por variar o método Somar, pois ao invés de receber os parâmetros e devolver o resultado, estou optando por acessar diretamente as propriedades no interior do método. E aqui cabe comentar um detalhe interessante: note que não usamos o método ObserveOnDispatcher na classe AsyncRelayCommand. Isso se deve ao fato de que o padrão MVVM faz com que a ViewModel seja completamente independente da View, e com isso, não conseguimos acessar seus respectivos controles e, consequentemente, não corremos risco nos depararmos novamente com aquela exceção que vimos acima.

public class CalculoViewModel : INotifyPropertyChanged
{
    public CalculoViewModel()
    {
        this.Calculo = new AsyncRelayCommand(Somar);
    }

    public ICommand Calculo { get; private set; }

    public string Valor1 { get; set;}

    public string Valor2 { get; set; }

    public string Resultado { get; set; }

    private void Somar()
    {
        Thread.Sleep(4000);

        this.Resultado =
            Convert.ToString(int.Parse(this.Valor1) + int.Parse(this.Valor2));
    }
}

Observação: Por questões de espaço eu preferi omitir a implementação necessária para notificar a alteração das propriedades (INotifyPropertyChanged). Isso continua sendo necessário, pois é assim que a View monitora toda e qualquer alteração que é realizada no interior da ViewModel para assim atualizar a UI.

Além das opções que vimos acima, ainda podemos necessitar que tenhamos um consumo periódico de alguma informação. Por exemplo, necessitamos monitorar serviço de cotação de valores, notícias de algum site (RSS), empregos, etc. Com isso, haverá uma tarefa sendo executando a cada X segundos, buscando as informações e, consequentemente, apresentando-as na tela para que o usuário possa visualizá-la.

Abaixo temos a ViewModel criada para atender este cenário. Temos uma propriedade chamada Noticias que retorna uma coleção do tipo ObservableCollection<Noticia>, qual será definida como fonte de dados de um controle ListBox da View (Xaml).

public class NoticiasViewModel
{
    public NoticiasViewModel()
    {
        this.Noticias = new ObservableCollection<Noticia>();
        this.MonitorarNoticias();
    }

    private void MonitorarNoticias()
    {
        Observable
            .Timer(TimeSpan.Zero, TimeSpan.FromSeconds(3))
            .Select(_ => BuscarNoticiasViaWeb())
            .ObserveOnDispatcher()
            .Subscribe(noticias => noticias.ForEach(n => this.Noticias.Add(n)));
    }

    private static List<Noticia> BuscarNoticiasViaWeb()
    {
        // Buscar notícias via Http
    }

    public ObservableCollection<Noticia> Noticias { get; set; }
}

Alguns novos operadores entram em cena aqui. O método Timer retorna uma sequência produzida (pelo método Select) a cada X segundos/minutos/horas. Note que voltamos a necessitar do método ObserveOnDispatcher, mesmo aqui, onde estamos utilizando MVVM. A questão é que quando definimos uma coleção como fonte de dados de algum controle, como é o caso do ListBox, ele envolve essa coleção em uma classe do tipo CollectionView, e esta herda diretamente da classe DispatcherObject, o que determina que ela tenha afinidade com a thread (Dispatcher) em que a mesma foi criada. Sendo assim, a não utilização do método ObserveOnDispatcher, vamos nos deparar com uma exceção do tipo NotSuppertedException, com a seguinte mensagem: This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.

Eis aqui algumas opções que temos para trabalharmos de forma assíncrona no WPF. Como disse antes, temos algumas mudanças que estão acontecendo nas linguagens que tendem a facilitar ainda mais a criação de código assíncrono, sem depender de qualquer recurso extra. De qualquer forma, essas opções já tornam o código bem mais expressivo e de fácil manutenção.

Tags: ,

Async | WPF

O impacto do limitador de conexões HTTP

by Israel Aece 8. February 2012 22:12

Internamente o .NET Framework possui uma classe chamada ServicePoint que controla as conexões HTTP que são realizadas. A sua criação é feita e gerenciada por uma outra classe, chamada de ServicePointManager, e ambas estão debaixo do namespace System.Net. A criação não se dá de forma desordenada, ou seja, ela é criada para atender um determinado recurso (página, serviço, imagem, etc.), e ao invés de desprezá-la, ela é armazenada e compartilhada para quando precisarmos, novamente, acessar aquele mesmo servidor.

Para falarmos de classes de mais alto nível, um exemplo é a classe HttpWebRequest que utilizamos para realizar requisições HTTP, que recorre internamente à classe ServicePointManager, e que por sua vez, faz todas verificações necessárias, e devolve um objeto ServicePoint pronto para ser utilizado. Como há uma única instância desta classe com aquele servidor, podemos ter a necessidade de realizar conexões simultâneas para ele. Por questões de performance, a especificação do protocolo HTTP determina que um cliente não deve manter mais do que duas conexões com um mesmo servidor.

Apesar desta sugestão, podemos ter a necessidade de aumentar esse valor para que possamos executar mais requisições concorrentes a um mesmo servidor. Pensando nisso, a Microsoft disponibiliza um configuração global, qual nos permite determinar a quantidade de conexões concorrentes que podemos realizar à um mesmo servidor. Para isso, há várias propriedades estáticas, que estão expostas a partir da classe ServicePointManager, e a propriedade que controla a quantidade de conexões é a DefaultConnectionLimit, onde o padrão é apenas 2 conexões, assim como sugere o HTTP/1.1.

Quando consumimos serviços WCF ou até mesmo serviços do tipo ASMX, não lidamos - diretamente - com as classes que vimos aqui, mas se analisarmos os bastidores das classes do WCF (ClientBase<TChannel>) e do ASMX (SoapHttpClientProtocol), veremos que eles, em algum momento, vai recorrer à classe HttpWebRequest e, consequentemente, estaremos sendo limitados caso haja a necessidade de realizar mais do que duas conexões concorrentes ao mesmo servidor.

Supondo que temos um serviço WCF e queremos consumí-lo em uma aplicação cliente, e para efeito dos testes, vamos criar 100 requisições concorrentes para o mesmo. Um pequeno detalhe, é que já definimos a quantidade mínima de threads para que o ThreadPool já as crie, nos antecipando e dizendo a ele quantas threads serão necessárias para executarmos o trabalho.

const int QtdeDeRequisicoes = 100;

ServicePointManager.DefaultConnectionLimit = 2;
ThreadPool.SetMinThreads(QtdeDeRequisicoes, QtdeDeRequisicoes);

var tasks = new Task[QtdeDeRequisicoes];
var sw = Stopwatch.StartNew();

for (int i = 0; i < QtdeDeRequisicoes; i++)
{
    tasks[i] =
        Task.Factory.StartNew<string>(o =>
        {
            Console.WriteLine("Inicio:\t" + o);

            using (var proxy = new ServiceReference1.ServiceClient())
                return proxy.GetData((int)o);
        }, i)
        .ContinueWith(t => Console.WriteLine("Fim:\t" + t.Result));
}

Task.WaitAll(tasks);
Console.WriteLine(sw.Elapsed);

Note que no código acima estamos explicitamente definindo que a aplicação cliente pode somente estabelecer duas conexões concorrentes com um mesmo servidor. Para o serviço de teste, ele levará, na minha máquina, cerca de 48 segundos. O gráfico abaixo ilustra a execução destas requisições. Enquanto a linha vermelha mostra a quantidade de requisições que chegaram até o serviço, a linha verde exibe a quantidade de requisições que estão sendo executadas por ele. Tudo isso sendo extraído do servidor onde o mesmo está hospedado.

Se mudarmos ligeiramente o código acima, definindo para 15 a propriedade DefaultConnectionLimit, o tempo para executar as mesmas 100 requisições cai para 20 segundos, ou seja, temos um ganho considerável. Novamente, através do gráfico abaixo temos as mesmas requisições em um tempo bem mais reduzido:

Observação: Apesar de facilitar a vida do cliente, esta configuração poderá comprometer o desempenho do serviço/servidor, pois estamos atendendo várias requisições que partem de um mesmo cliente, exacerbando o serviço com requisições que poderiam estar distribuídas entre outros interessados em consumir este mesmo serviço.

É importante mencionar que o cliente criado para consumir o serviço WCF de exemplo, é uma aplicação Windows (Console). Quando a aplicação cliente é uma aplicação Web (ASP.NET), também podemos ser influenciado por esta configuração, mas neste caso, ele está dimensionado de uma forma diferente, ou seja, em sua configuração padrão, no interior da classe HttpRuntime, ele define a propriedade DefaultConnectionLimit como 12 * quantidade de CPUs da máquina atual. Sendo assim, qualquer conexão HTTP que você realize através desta aplicação Web, você estará "limitado" à esta quantidade de conexões.

Finalmente, para efeito de testes, você não pode ter o cliente e o serviço na mesma máquina, pois a classe ServicePointManager avalia se o serviço está hospedado localmente, e se estiver, retorna a constante Int32.MaxValue (2147483647) como quantidade de conexões simultâneas, sendo um valor que dificilmente será atingido.

Tags: , ,

WCF

WCF Web API - Suporte à JSONP

by Israel Aece 6. February 2012 19:52

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 <script />, 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:

<!DOCTYPE html>
<html>
<head>
<script src="jquery-1.5.1.js" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
    $(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('<li>' + cotacao.Moeda + ': ' + cotacao.ValorEmReal + '</li>');
                });

                $('<ul/>', { html: itens.join('') }).appendTo($('#Cotacoes'));
        }
    });
});
</script>
</head>
<body>
    <div id="Cotacoes">
    </div>
</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.

Tags: , , ,

WCF

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

Indicações

Introdução ao ASP.NET Web API - e-Book