segunda-feira, 8 de dezembro de 2014

MongoDB + ASP.NET Web API 2

No dia-dia de um desenvolvedor é bem comum a necessidade de criar uma API com pequena ou baixa complexidade, ambientes que possam entregar informações a qualquer dispositivo e plataforma vem se tornando cada vez mais rotineiro.
Através dessa necessidade que a microsoft criou um Framework bastante fácil para desenvolver serviços HTTP http://www.asp.net/web-api





A primeira vez que tive contato com Web Api ainda na versão 1 fiquei muito animado porque consegui ver muitos ganhos principalmente em agilidade de desenvolvimento em relação aos serviços RESTful desenvolvidos com WCF até então.  
Para você que não teve a oportunidade de entrar nesse mundo de serviços RESTful te recomendo a começar com Web Api, é bastante fácil e com pouco programado você vai conseguir ver um resultado bastante interessante. Esse tutorial escrito por Mike Wasson http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api no site oficial do Asp Net vai perceber como é simples criar e consumir o teu serviço criado. Reforço a sua atenção no tutorial do Mike em que ele cria um client em Javascript para teste dos serviços, algo também muito utilizado ou talvez o mais utilizado ... fácil não? 



A minha intenção com esse post além de apresentar o Asp Net Web Api é mostrar a você que você pode também acoplar ele a um banco de dados NoSQL e tornar o seu serviço ainda mais rápido, é claro isso vai depender se a tua analise comporta um banco de dados NoSQL.

Sirva-se de um café e vamos codificar (...) Primeiramente iremos precisar criar um projeto Web Api. Eu optei por criar um projeto "Empty". 




Após criar o projeto já também criei um controller chamado "NOTICIA" e também um modelo também chamado "NOTICIA", perceba que me antecipei e já criei uma pasta chamada "MONGODB". A minha solution ficou mais ou menos assim ...

Se você executar o projeto vai ver que o serviço já esta funcionando porém você não implementou nenhum dos métodos no controller vai receber um erro ou se você optou por criar um controller já com as funções de read/write actions vai ter um template de alguns métodos previamente implementado, fica a sua escolha. 
Com o nosso serviço funcionando vamos agora implementar um pequeno repositório para facilitar a nossa vida ao escrever as nossas consultar no MongoDB.

 public interface IRepository<TEntity> where TEntity : EntityBase   
 {   
    bool Insert(TEntity entity);   
    List<TEntity> SearchFor(Expression<Func<TEntity, bool>> predicate);   
    List<TEntity> GetAll();   
    TEntity GetById(ObjectId id);   
 }   

 public abstract class EntityBase   
 {   
    [BsonId]   
    [BsonRepresentation(BsonType.ObjectId)]   
    public string Id { get; set; }   
 }   

Implementamos uma interface com os métodos que teremos disponíveis no repositório e também uma classe abstract para utilizar em nossos modelos, logo você vai perceber que não será necessário criar um atributo "Id" para nossos modelos sendo que apenas devemos herdar a nossa classe "EntityBase".

Exemplo:
 public class Noticia : EntityBase  
 {  
    public string Titulo { get; set; }  
    public string Conteudo { get; set; }  
    public string Autor { get; set; }  
 }  

Não devemos esquecer de baixar o pacote de DLL do drive do MongoDB para C# é bastante simples e pode ser baixado no
nuGet https://www.nuget.org/packages/mongocsharpdriver/1.9.2

   public class MongoDbRepository<TEntity> : IRepository<TEntity> where TEntity : EntityBase  
   {  
     private MongoDatabase _Database;  
     private MongoCollection<TEntity> _Collection;  
     public MongoDbRepository()  
     {  
       GetDatabase();  
       GetCollection();  
     }  
     private void GetDatabase()  
     {  
       var client = new MongoClient(GetConnectionString());  
       var server = client.GetServer();  
       _Database = server.GetDatabase(GetDatabaseName());  
     }  
     private string GetConnectionString()  
     {  
       return WebConfigurationManager.AppSettings["MongoDbConnectionString"].Replace("{DB_NAME}", GetDatabaseName());  
     }  
     private string GetDatabaseName()  
     {  
       return WebConfigurationManager.AppSettings["MongoDbDatabaseName"];  
     }  
     private void GetCollection()  
     {  
       _Collection = _Database  
         .GetCollection<TEntity>(typeof(TEntity).Name);  
     }  
     public bool Insert(TEntity entity)  
     {  
       entity.Id = ObjectId.GenerateNewId().ToString();  
       return _Collection.Insert(entity).Ok;  
     }  
     public List<TEntity> SearchFor(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)  
     {  
       return _Collection  
           .AsQueryable<TEntity>()  
             .Where(predicate.Compile())  
               .ToList();  
     }  
     public List<TEntity> GetAll()  
     {  
       return _Collection.FindAllAs<TEntity>().ToList();  
     }  
     public TEntity GetById(ObjectId id)  
     {  
       return _Collection.FindOneByIdAs<TEntity>(id);  
     }  
   }  
Se você já teve oportunidade de trabalhar com repositórios com o EF por exemplo vai perceber alguma semelhança pois utilizamos de expressões linq para acesso aos documentos. Os três métodos privados ficam responsáveis pela configuração do servidor do mongo.
Eu implementei buscando dentro do web.config apenas para uma melhor organização isso não impediria em nada no funcionamento se estivesse diretamente no código a connection do banco.


O servidor do MongoDB poderia estar localmente instalado como escrevi um tutorial a algum tempo falando especificamente sobre essa instalalção no windows. http://jr-encode.blogspot.com.br/2013/03/instal-mongodb-no-windows-como-servico.html.
O processo de instalação é bastante simples mas temos uma outra opção se você não quiser instalar o serviço do mongoDB na sua rede você pode estar utilizando um serviço que inicialmente é gratuito até um certo tamanho de banco e tendo a opção de escalar conforme sua necessidade. É um serviço muito bom pude utilizar em um projeto recentemente e não tive problemas. Você pode estar lendo mais a respeito https://mongolab.com/, após criar um login e senha no Mongolab você terá que criar um novo database.


Recomendo a você a criar um single-node que tem uma opção de até 0.5G gratuito, você terá que informar ao mongolab o nome do seu database a próxima pagina o mongolab irá mostrar sua URL de acesso ao database onde você terá que substituir na config do repositório, depois disso é SUCESSO, você terá um banco NoSQL online tendo a opção de escalar muito dependendo da necessidade e crescimento da sua API.
Com o nosso repositório configurado podemos agora codificar os métodos do nosso controller de noticia.

   public class NoticiaController : ApiController  
   {  
     private IRepository<Noticia> _RepNoticia;  
     public NoticiaController()  
     {  
       _RepNoticia = new MongoDbRepository<Noticia>();  
     }  
     /// <summary>  
     /// Retorna todas as noticias.  
     /// </summary>  
     /// <returns></returns  
     [Route("")]  
     public IEnumerable<Noticia> Get()  
     {  
       return _RepNoticia.GetAll();  
     }  
     /// <summary>  
     /// Retorna uma noticia filtrada pelo Id  
     /// </summary>  
     /// <param name="id">Id</param>  
     /// <returns></returns>  
     [Route("{id}")]  
     public Noticia Get(ObjectId id)  
     {  
       return _RepNoticia.GetById(id);  
     }  
     /// <summary>  
     /// Retorna todas as noticias filtrando por alguma palavra contida no titulo.  
     /// </summary>  
     /// <param name="titulo">Titulo</param>  
     /// <returns></returns>  
     [Route("titulo/{titulo}")]  
     public IEnumerable<Noticia> Get(string titulo)  
     {  
       return _RepNoticia.SearchFor(f => f.Titulo.Contains(titulo)).ToList();  
     }  
     /// <summary>  
     /// Grava uma noticia  
     /// </summary>  
     /// <param name="value"></param>  
     [Route("")]  
     public HttpResponseMessage Post(Noticia noticia)  
     {  
       try  
       {  
         _RepNoticia.Insert(noticia);  
         return Request.CreateResponse(HttpStatusCode.OK, "Noticia gravada com sucesso");  
       }  
       catch (Exception ex)  
       {  
         return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);  
       }  
     }  
   }  
As chamadas para o repositório fica de forma limpa e de fácil entendimento, basicamente temos quatro métodos de teste, são eles: Get (retorna todos), Get (retorna filtrado por id), Get (retorna todos por alguma palavra contida titulo), Post (Grava uma noticia). Outro detalhe que possa parecer diferente é a utilização de "RoutePrefix" na anotação do controller. Isso irá mudar a URL de acesso no router que deverá ser acessada agora assim: http://localhost/api/public/v1/noticias/   dessa forma você consegue padronizar melhor as chamadas da sua API, acredito que isso é mais uma boa pratica porém se você tiver uma maior quantidade de controller vai perceber que começa a ficar "complicada" a organização da API. Segue um outro interessante tutorial falando sobre "RouterPrefix" também escrito pelo Mike Wasson http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Outra dica interessante que costumo fazer é como geralmente utilizo retorno de formato JSON até porque é bem mais tranquilo para você trabalhar em Javascript por exemplo, removendo o retorno de XML e também formatando o retorno do JSON algo que vai te ajudar bastante na leitura em ambiente de desenvolvimento.

Global.asax.cs (Método register)

Espero ter ajudado, Fique a vontade para manter contato.
Abraço,

Nenhum comentário:

Postar um comentário