Introdução a Asp.Net WebAPI    

No período de inatividade do Blog, muitas coisas aconteceram no Asp.Net, inclusive uma nova versão foi lançada com muitas melhorias. O Diagrama que mostrei neste post já está desatualizado e vou tentar correr atrás do tempo perdido para falar de alguns temas por aqui.

Introdução

O Asp.Net já está bastante consolidado com uma arquitetura sólida e extensível, permitindo o desenvolvimento seguindo diversos padrões e paradigmas. Como mencionado no post citado, sobre o Asp.Net existem varias outras camadas que possibilitam diferentes abordagens para desenvolver diferentes tipos de projetos, sendo que todas elas são válidas de acordo com o objetivo da aplicação. De forma rápida podemos citar algumas destas formas de desenvolvimento que o Asp.Net fornece: Web Forms, Asp.Net MVC, Dynamic Data, Web Pages e agora também o Asp.Net WebAPI. É exatamente sobre essa última forma que falaremos ao longo desse post . Farei aqui apenas de uma introdução teórica, e nos próximos posts mostrarei alguns exemplos.

É comum, quando ouvimos falar de tecnologias Web, pensarmos em sites ou sistemas que rodem em Browser, porém, Web é muito mais que isso. Arquiteturas de Nuvem, como o Azure da Microsoft ou o AWS da Amazon são bons exemplos de cenários onde hospedamos serviços na Web, com o objetivo de serem consumidos de qualquer origem, e não apenas de sites. Esse tipo de arquitetura orientada a serviço (SOA na sigla em inglês) além de fornecer um grande ganho em escalabilidade, sazonalidade, especialização, entre outros benefícios, pode ser consumido por muitos tipos de aplicações, como Windows, Cobol, Console ou qualquer outra tecnologia que não rode em Browser e necessite de dados centralizados e serviços dedicados.

Um bom exemplo de aplicação que consome serviços na Web em grande escala, e que é muito popular nos dias de hoje, são os aplicativos para SmartPhone. A grande maioria desses aplicativos está sempre conectada, consumindo serviços e exibindo-os para o usuário como se eles fossem locais.

Imagine agora você ter uma ótima ferramenta para prover serviços para toda essa gama de aplicativos, seguindo sempre uma mesma arquitetura e o mesmo padrão. Foi pensando nisso que a Microsoft criou o Asp.Net WebAPI, que nos dá todas as ferramentas e facilidades necessárias para conseguirmos alcançar todos esses objetivos

Como o próprio nome diz, WebAPI é forma que a equipe do Asp.Net nos dá para disponibilizar uma API (Application programming interface) hospedada na Web, ou seja, abrir uma porta na sua aplicação para que outras aplicações e serviços possam interagir com ela por meio de serviços HTTP. Para quem é desenvolvedor Windows esse conceito é muito comum, pois muitas vezes dependemos de APIs do sistema operacional para desenvolver alguma funcionalidade mais avançada.

Entendendo Asp.Net WebAPI

O Asp.Net WebAPI, diferente de outras tecnologias Web da Microsoft, como o WebForms, Asp.Net MVC ou WebPage, não é construído diretamente sobre a plataforma Asp.Net, ele é, na realidade, uma abstração feita sobre o Asp.Net MVC 4. Isso quer dizer que além de você fazer uso de toda a tecnologia compartilhada pela base do Asp.Net, você terá em mãos também toda a tecnologia especializada e já consolidada do Asp.Net MVC. Na prática, um projeto Asp.Net WebAPI é um projeto Asp.Net MVC com itens/arquivos a mais. Ele contém toda a configuração padrão de um projeto MVC normal. Isso é muito proveito, uma vez que você poderá fazer um site e no mesmo projeto disponibilizar serviços padronizados.

Encontramos no Asp.Net WebAPI termos muito comuns de aplicativos Asp.Net MVC simples. Por exemplo, nossos métodos são expostos através de Controllers e são alcançados através de Rotas, as diferenças estão basicamente em classes bases, que são específicas para cada camada.

Os Controllers do WebAPI, por exemplo, herdam de ApiController ao invés de simplesmente Controller, que é o caso dos controllers de uma aplicação padrão MVC. Por sua vez, os controllers do WebAPI não retornarão Views como ActionResult, pois não é esperado que serviços retornem telas ou relatórios, mas apenas dados. As Rotas também têm diferenças: sua configuração se dá através do método routes.MapHttpRoute ao invés do routes.MapRoute, dessa forma permitindo que o mesmo aplicativo/site tenha rotas configuradas para o WebAPI e para Controllers MVC padrões.

É interessante saber também que com o WebAPI você não precisa ter uma infraestrutura Web disponível para sua aplicação, ou seja, você não precisa do IIS para disponibilizar seus serviços REST em um protocolo HTTP. Com poucas linhas de código você consegue expor serviços em um Console Application, ou outro tipo de aplicação .Net, fazendo o que é chamado de Self-Host WebAPI. Apenas baixando um pacote no NuGet você já tem tudo que precisa para criar serviços sem IIS, apenas com o .Net instalado.

O que são serviços REST?

Você deve ter reparado que comentei algumas vezes de serviço REST até agora no post. Isso porque o WebAPI foi criado principalmente para geração de serviços REST. Apesar de ter citado o termo algumas vezes, ainda não deixei claro o que seria esse tipo de serviço. Tentarei agora fazer uma breve explicação desse conceito de serviço, já muito utilizado por diversos desenvolvedores, mas que é sempre bom ser esclarecido.

Já ficou evidente que o principal ganho ao desenvolver projetos com Asp.Net WebAPI é a facilidade em desenvolver serviços REST (Representational State Transfer). Apesar desse não ser o único tipo de serviço web possível com WebAPI, nitidamente essa é o tipo de serviço recomendado pela Microsoft ao utilizar WebAPI, como veremos a seguir. Para desenvolver serviços RPC (Remote Procedure Call), por exemplo, os WebServices ASMX ou WCF já são bem aptos e fáceis, enquanto que, por outro lado, desenvolver serviços REST em ASMX ou WCF não é tão simples como com WebAPI.

Para quem não sabe, a grande diferença entre serviços REST e RPC é que enquanto em REST os serviços expõem seus dados como recursos em URI e os clientes interagem com esses dados através de verbos HTTP, em RPC os serviços expõem diferentes métodos organizados de forma aleatória em uma ou mais URIs.

Por exemplo, um serviço REST teria sempre a mesma URI no formato MeuEndereco/MeuDado e de acordo com o verbo HTTP (POST, GET, PUT, DELETE), a aplicação poderia responder as requisições do Cliente sobre o “MeuDado”. Ou seja, o cliente pode obter um registro através do verbo GET ou salvar o mesmo registro com o verbo Put ou Post.

Por outro lado, no RPC, se fosse implementado em ASMX, seria algo assim MeuEndereco/MeuServico/ObterDado, MeuEndereco/MeuServico/SalvarDado, MeuEndereco/MeuServico/OutroMetodo, etc. Perceba que o final do endereço é um Método, e não um recurso, como é o caso do REST.

Serviços REST são muito úteis para expor serviços com a função CRUD sobre um dado, pois os principais verbos HTTP servem exatamente como os principais comandos SQL (SELECT, INSERT, UPDATE e DELETE). Mas é importante ter em mente que serviços REST não estão restritos a apenas operações CRUD.

Quando criamos um serviço REST, respeitamos todas as formas de comunicação estabelecidas pelo protocolo HTTP. Visitando esse post você pode ter uma noção de como funciona o HTTP. Além disso, você pode encontrar todos os códigos de Status pré-definidos em diversos sites da Web. No .Net existe o enum HttpStatusCode, no namespace System.Net o nome e o código de todos esses Status.

Também precisamos ter em mente ao modelarmos serviços REST que eles devem ser planejados para não ter estados, como Sessions, e devido a isso, cada Requisição deve ter toda a informação necessária para que o servidor possa processar a mensagem. E para ser padrão, ele deve retornar dados compreendidos na maioria das linguagens, ou seja, ele deve retornar geralmente XML ou JSON. É muito comum, quando criamos serviços seguindo o padrão REST, chamá-los de RESTFul.

Em quais situações utilizar o WebAPI?

Não é difícil responder a pergunta “Em quais situações utilizar o WebAPI”. Por ser um framework de serviço, qualquer oportunidade de utilizar uma camada de serviço pode ser exposta via WebAPI. Eu já citei o uso em SmartPhones, que talvez seja o mais comum, mas podemos encontrar muitos outros lugares. Sites como o Twitter, por exemplo, disponibilizam uma API inteira para nossos softwares interagirem com seus serviços. Sites de notícias pode disponibilizar APIs que vão além da funcionalidade exposta por um RSS, por exemplo, possibilitando sistemas de terceiros ranquearem e comentarem notícias. Aplicativos de SmartTV também têm se popularizado e também são consumidores de APIs. A lista seria enorme se colocássemos todas as possibilidades.

O importante é saber o que o WebAPI oferece e dessa forma poderíamos visualizar oportunidades de aplicação de serviços feitos em WebAPI em ainda mais lugares.

Por exemplo, é possível expor serviços OData via WebAPI de forma tão simples quanto expor serviços RESTFul simples. Para alcançarmos tal objetivo, precisamos apenas retornar um interface IQueryable em nossos métodos, que a aplicação cliente teria condições de pesquisar, ordenar e filtrar os registros de forma automática.

Obs: OData (Open Data Protocol) é um protocolo Web para pesquisar e atualizar dados através de tecnologias conhecidas na Web. É uma forma, por exemplo, de expor tabelas um Banco de Dado direto no protocolo HTTP e permitir que as consulta sejam feitas diretamente via HTTP.

É isso pessoal, hoje apenas introduzi o Web API, nos próximos posts mostrarei alguns exemplos.

Até logo!

15. January 2013 04:13 by Frederico B. Emídio | Comments (950) | Permalink

Executando serviço em Background na infraestrutura do Asp.Net    

Olá pessoal!

Hoje eu vou abordar um tema não muito ortodoxo: As possíveis formas de “hospedar” um serviço na infraestrutura do Asp.Net.

Entenda como serviço nesse contexto uma tarefa que roda de tempos em tempos para realizar uma tarefa qualquer, que não dependa de uma ação do usuário, como o disparo de e-mails de newsletter, ou o que for. Não entenda serviço aqui como um WebService.

Digo que não é ortodoxo porque nesse caso, você deveria hospedar seu serviço em um WindowsService ou Console Application que pudesse ser iniciado pelo scheduler do Windows, pois essas são as formas normais de criar serviços que rodam por tempo indeterminado e que executam tarefas de tempos em tempos.

Mas então, porque criar um “serviço” na estrutura do Asp.Net? A resposta é simples: para as opções citadas acima, você precisa ter um servidor dedicado ou um Azure, o que muitas vezes não temos condições de ter, porque o projeto simplesmente não paga uma infraestrutura dessas. Muitas vezes hospedamos nossos sites em servidores baratos que não nos fornece nada mais que o IIS para hospedar nosso site. (Eu acredito que essa seja a realidade da grande maioria dos desenvolvedores).

Ao longo deste post, vou mostrar algumas formas de fazer esses “serviços” funcionarem, e expor os prós e os contras de cada um.

Vamos lá, mãos a obra!

Exemplo 1 – Criando nova Thread no Application Start

A primeira forma que vou demonstrar é bem simples. Vamos criar uma nova Thread na aplicação Asp.Net no momento que ela for iniciada, e essa Thread terá a lógica para manter o serviço sempre ativo e a própria lógica do processamento, o exemplo do código está abaixo:

   1: protected void Application_Start()
   2: {
   3:     AreaRegistration.RegisterAllAreas();
   4:     RegisterGlobalFilters(GlobalFilters.Filters);
   5:     RegisterRoutes(RouteTable.Routes);
   6:     ThreadPool.QueueUserWorkItem(Executar);
   7: }
   8:  
   9: public void Executar(object state)
  10: {
  11:     while (true)
  12:     {
  13:         //Lógica do Serviço
  14:         Thread.Sleep(20000);
  15:     }
  16: }

O código é bem simples. Na linha 6 eu crio uma nova Thread e  a lógica do “serviço” está no método Executar. Todo esse código está no Global.asax.

Prós:

  • Criação bem simples
  • Os usuários do site não são diretamente afetados pelo processo.

Contras:

  • Uma exceção não tratada em uma Thread que não está associada a um Request derrubará todo o processo.
  • Se você estiver em um WebFarm, você poderá criar a Thread em vários servidores, fazendo múltiplas instância do seu processo ser iniciado e mais de uma tarefa pode realizar o mesmo processamento.
  • O AppDomain do seu site pode cair por uma série de motivos e levar junto seu serviço, eventualmente corrompendo dados.

Exemplo 2 – Realizando o processamento em um Request

Essa segunda forma também é simples, porém eu a acho extremamente fraca e feia, mas não deixa de ser uma possibilidade. Ela verifica a necessidade de rodar um serviço em todos os Requests que chegam ao site, neste caso validado no momento de renderizar uma View sempre utilizada. Meu código está no meu arquivo de Layout do Asp.Net MVC (MasterPage se for WebForm):

   1: @functions{
   2:     public bool DeveExecutar(){
   3:         //Lógica para verificar se deve executar ou não, validando algo em cache ou banco de dados, por exemplo
   4:         return true;
   5:     }
   6: }
   7:  
   8: @if (DeveExecutar())
   9: {
  10:     //Lógica do serviço, pode ser em uma nova thread
  11: }

Acima estou mostrando um código em Razor onde eu preciso verificar se está na hora de executar o serviço, podendo fazer isso de diversas formas possíveis, como dito no comentário da linha 3. E também seguindo o comentário da linha 10, para não onerar demais o usuário com o processamento do serviço, você pode iniciar uma Thread nova para realizar sua tarefa.

Prós:

  • Para não falar que é nenhum, é ligeiramente fácil para desenvolver, especialmente em WebForm, que seria apenas codificar uma lógica semelhante à mostrada acima no Page Load da MasterPage.

Contras:

  • Todos os do Exemplo 1.
  • Por depender de Requests, se o seu site não tiver nenhum Request num grande período de tempo, o serviço não será executado.
  • Por interceptar um Request, o usuário poderá perceber que o site está lendo, pois o Response não está voltando tão rápido como deveria.

Exemplo 3 – Realizando o processamento em um Request – Modo 2

O exemplo 3 tem toda a lógica igual ao do Exemplo 3 e praticamente os mesmos Prós e Contras, a única coisa que muda seria a forma de desenvolver. Nesse exemplo, estou tratando o Evento BeginRequest do Global.asax.

   1: protected void Application_BeginRequest()
   2: {
   3:    if (DeveExecutar())
   4:    {
   5:        //Lógica do serviço, pode ser em uma nova thread
   6:    }
   7: }
   8:  
   9: public bool DeveExecutar()
  10: {
  11:    //Lógica para verificar se deve executar ou não, validando algo em cache ou banco de dados, por exemplo
  12:    return true;
  13: }

Esse modo de fazer tem ainda mais um contra:

  • O evento BeginRequest é chamado para qualquer Request, ou seja, para cada Recurso (imagem, js, etc) chamado, não só o Controller, esse método será chamado, o que pode crescer consideravelmente o tempo para carregar uma página.

Até agora vimos que há muito mais problemas do que soluções para criar esse tipo de serviços, vamos mais um pouco a fundo nos problemas, e assim depois poderemos procurar uma solução um pouco mais satisfatória que as definidas acima.

Motivos pelos quais o AppDomain pode cair

No último contra do Exemplo 1, eu comentei que o AppDomain pode cair e causar problemas para sua Thread, vamos ver alguns caso que isso pode acontecer.

O Asp.Net pode derrubar seu AppDomain pelos seguintes motivos, entre outros:

  • Quando você modifica o Web.Config do site o Asp.Net vai realizar o recycle do AppDomain.
  • O IIS vai por conta própria reciclar todo o processo a cada 29 horas, derrubando todos os AppDomain sob ele.
  • Em muitos servidores, é comum o IIS está configurado para derrubar o Application Pool depois de algum período de inatividade, ou seja, se depois de 20 minutos, por exemplo, sem receber um Request, o AppDomain pode ser derrubado.

Nesse caso, você pode estar se perguntando: Esses problemas não podem ocorrer também para Requests normais? O AppDomain não pode cair ou ser reciclado durante a execução de um Request?

Bom, quando o Asp.Net/IIS resolve derrubar/reciclar um AppDomain, ele procura descarregar todos os Requests pendentes e dar um tempo para eles terminarem seus trabalhos. Ele dá esse tempo para os códigos que ele sabe que estão rodando, e normalmente os código que ele “sabe” são os códigos de Requests padrões.

Basicamente, o problema apontado no Exemplo 1 com o AppDomain é que o Asp.Net/IIS não sabe que o seu código está rodando, por não ser um Request normal..

Informando ao Asp.Net que você tem um código especial rodando

Depois de ver tanto problema, vamos começar a estudar uma solução.

Existe uma forma fácil de informar ao Asp.Net sobre o seu código. No namespace  System.Web.Hosting  existe uma classe chamada HostingEnvironment que segundo o MSDN:

Provides application-management functions and application services to a managed application within its application domain

Ou seja, a classe te fornece funções e serviços para gerenciar uma aplicação dentro de um AppDomain.

Dessa classe, precisamos conhecer, para o nosso objetivo, apenas dois métodos: RegisterObject e UnregisterObject.

A função desses métodos é registrar e desregistrar um objeto a lista de “Códigos conhecidos” do Asp.Net.

Esses métodos recebem como parâmetro apenas um objeto que implementa a interface IRegisteredObject, que contém apenas um método, conforme snippet abaixo:

   1: public interface IRegisteredObject
   2: {
   3:     void Stop(bool immediate);
   4: }

Quando o Asp.Net vai derrubar o AppDomain, ele primeiro invoca o método Stop de todos os objetos registrados naquele AppDomain. Em geral ele chama o método duas vezes, primeiro com o parâmetro immediate igual a false, o que te dá um tempo para terminar o seu trabalho, tempo esse que é um total de 30 segundos para todos os objetos registrados terminarem o que estão fazendo, e uma segunda vez é chamado após esses 30 segundos passarem, e desta vez com o parâmetro igual a true, o que seguinifica que você precisa terminar seu trabalho agora, porque seu AppDomain está prestes a sumir do mapa.

O segredo é: Uma vez que o Asp.Net chama o método Stop do seu objeto, você deve impedir que esse método retorne até que seu trabalho tenha terminado. Quando o trabalho terminar, você deve desregistrar seu objeto.

Com essa explicação, vamos ao quarto exemplo:

Exemplo 4 – Criando o serviço em um objeto registrado no Asp.Net

Esse exemplo é melhor que todos os anteriores, pois continua sendo simples, e alguns dos contras são superados:

   1: public class Servico : IRegisteredObject
   2: {
   3:    private readonly object _lock = new object();
   4:    private bool _derrubando;
   5:    public Servico()
   6:    {
   7:        HostingEnvironment.RegisterObject(this);
   8:    }
   9:    public void Stop(bool immediate)
  10:    {
  11:        lock (_lock)
  12:        {
  13:            _derrubando = true;
  14:        }
  15:        HostingEnvironment.UnregisterObject(this);
  16:    }
  17:    public void Executar()
  18:    {
  19:        while (true)
  20:        {
  21:            lock (_lock)
  22:            {
  23:                if (_derrubando)
  24:                {
  25:                    return;
  26:                }
  27:                //Lógica do serviço
  28:            }
  29:            Thread.Sleep(60000);
  30:        }
  31:    }
  32: }

Veja que adicionei um Lock no método Stop e dessa forma impeço que esse método retorne caso esteja no meio de uma execução do método Executar.

No Global.asax eu chamo a minha classe do serviço:

   1: protected void Application_Start()
   2: {
   3:     AreaRegistration.RegisterAllAreas();
   4:     RegisterGlobalFilters(GlobalFilters.Filters);
   5:     RegisterRoutes(RouteTable.Routes);
   6:     ThreadPool.QueueUserWorkItem(state => new Servico());
   7: }

Veja que na linha 6 crio uma Thread nova para o meu objeto, mas agora ele estará registrado no Asp.Net e seu fim não será tão repentino como seria antes.

Agora meus Prós e Contras ficarão da seguinte forma:

Prós:

  • Os usuários do site não são diretamente afetados pelo processo.
  • Terei mais controle do tempo de vida do meu “serviço”

Contras:

  • Uma exceção não tratada em uma Thread que não está associada a um Request derrubará todo o processo.
  • Se você estiver em um WebFarm, você poderá criar a Thread em vários servidores, fazendo multiplas instância do seu processo ser iniciado e mais de uma tarefa pode realizar o mesmo processamento.
  • Para utilizar o RegisterObject é necessário que o site esteja rodando com FullTrust

Essa abordagem seria a mais próxima do ideal. Temos ainda o problema das Exceções, que você deve ter cuidado, e a questão do WebFarm, que você terá que fazer um controle mais manual, mas que foge do objetivo desse post, mas agora temos ao menos mais controle sobre o AppDomain.

Também temos um novo problema, que é a necessidade do FullTrust. Porém, até servidores baratos aceitam FullTrust e muitos frameworks que utilizamos no desenvolvimento também exige FullTrust, então acredito que esse é um problema secundário.

É importante falar ainda que o AppDomain pode cair por outros problemas criticos, e você não será avisado, o que pode ainda corromper seus dados no serviço. Por exemplo, o AppDomain por cair se alguém tropeçar no cabo de força ou se o Windows mostrar uma tela azul, ou por qualquer outro motivo desastroso. Porém, esses riscos você também pode enfrentar em Windows Services normais.

Exemplo 5 – Apenas uma abordagem alternativa ao Exemplo 4

Nesse exemplo venho apenas mostrar um alternativa aos Loops com While(true) que estou utilizando, mas a solução é ainda a do Exemplo 4. Nesse exemplo vou utilizar um Timer no  lugar do While(true). Acredito que essa abordagem seja um pouco mais elegante.

 

   1: using System;
   2: using System.Threading;
   3: namespace Exemplo5
   4: {
   5:     public static class MeuTimer
   6:     {
   7:         private static readonly Timer _timer = new Timer(OnTimerElapsed);
   8:         private static readonly Servico _servico = new Servico();
   9:         public static void Start()
  10:         {
  11:             _timer.Change(TimeSpan.Zero, TimeSpan.FromMilliseconds(60000));
  12:         }
  13:         private static void OnTimerElapsed(object sender)
  14:         {
  15:             _servico.Executar();
  16:         }
  17:     }
  18: }

Só preciso mudar uma linha no meu Global.asax:

   1: protected void Application_Start()
   2: {
   3:     AreaRegistration.RegisterAllAreas();
   4:     RegisterGlobalFilters(GlobalFilters.Filters);
   5:     RegisterRoutes(RouteTable.Routes);
   6:     MeuTimer.Start();
   7: }

Agora na linha 6 eu apenas chamo o método Start do meu Timer, e o meu serviço rodará de tempos em tempos de acordo com o meu Timer.

Conclusão

Vimos algumas alternativas para utilizar serviços na infraestrutura do Asp.Net, mesmo sabendo que essa deva ser a última opção, é bom saber quais alternativas temos. Nitidamente, eu prefiro utilizar a última abordagem, ela é a mais madura de todas.

É importante saber também que essas soluções funcionam tanto em WebForm como em MVC.

Por hoje é isso pessoal.

Segue o fonte dos exemplos:

Valeu!

Obs.: Esse post foi inspirado neste outro post

29. February 2012 23:32 by Frederico B. Emídio | Comments (49) | Permalink

Sobre mim

Minha Imagem

Meu nome é Frederico Batista Emídio, trabalho com desenvolvimento de sistemas profissionalmente a oito anos, porém, já faço sites pessoais a pelo menos dez anos.

Para saber mais clique aqui.

Páginas

Calendário

<<  December 2014  >>
MoTuWeThFrSaSu
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

Visualizar posts em um Calendário
Sigua @fredemidio

MCP Asp.NET