quarta-feira, 10 de dezembro de 2014

Upload arquivos usando o AWS SDK for .NET (C#)

É bastante comum em diferentes tipos de projetos a necessidade de fazer upload de algum tipo de arquivo, sendo ele uma imagem ou algum outro. A forma mais fácil ou a mais utilizada seria fazer upload desse arquivo para uma determinada pasta do projeto, e se você estiver utilizando o IIS como servidor de pagina ou mesmo se for APACHE por exemplo de ambas as formas você estaria fazendo teu servidor entregar o conteúdo estático que nesse caso seria o arquivo que foi feito upload. Não vejo nenhum problema nisso quando falamos de poucos arquivos mas se a sua aplicação tiver uma necessidade maior de performance na entrega ou até mesmo a quantidade for bastante grande gerando um tamanho significante no disco do teu servidor você teria que optar por outra coisa.
Para isso a AMAZON AWS oferece um serviço bastante barato e rápido que pode ser utilizado para diversas outras funcionalidades. Apresentação do serviço no site oficial: http://aws.amazon.com/pt/s3/
A intenção desse post é exatamente mostrar como é simples a integração com o serviço "Simple Storage Service" ou como é conhecido AMAZON S3.

Primeiramente eu criei uma classe estática e nela vamos codificar o nosso método de uploadFile. A organização de onde ficaria melhor a classe fica a critério da arquitetura que você está trabalhando.
Antes de começar a codificar vamos fazer referencia ao SDK da amazon aws, você pode encontrar ele facilmente no Nuget.https://www.nuget.org/packages/AWSSDK/2.3.12 ou se você prefere por command.


   public static class UploadAmazon  
   {  
     private static IAmazonS3 client;  
     public static HttpStatusCode UploadFile(Stream file, string type, string nomeImagem)  
     {  
       using (client = AWSClientFactory.CreateAmazonS3Client("****key****", "***secret key****"))  
       {  
         PutObjectRequest request = new PutObjectRequest();  
         request.BucketName = "MeusArquivos";      //nome do bucket name  
         request.CannedACL = S3CannedACL.PublicRead;  //permissões  
         request.Key = "Arquivos/" + nomeImagem;    //diretorio e nome do arquivo  
         request.ContentType = type;         //type do arquivo  
         request.InputStream = file;         //Stream do arquivo  
         return client.PutObject(request).HttpStatusCode;  
       }  
     }  
   }  
Veja que o código é bastante simples =)
Apenas se atentar alguns detalhes, a propriedade "BucketName" eu coloquei fixa porém você poderia fazer uma verificação se esse "BucketName" se ele já não existe na sua storage, ficaria mais ou menos assim:
 private static bool IsCheckBucket(string bucketName)  
 {  
     ListBucketsResponse response = client.ListBuckets();  
     bool found = false;  
     foreach (S3Bucket bucket in response.Buckets)  
     {  
         if (bucket.BucketName == bucketName)  
         {  
           found = true;  
           break;  
         }  
      }  
      return found;  
  }  
(...) e para criar o BucketName
   public void CreateBucket(string bucketName)  
     {  
       if (!this.IsCheckBucket(bucketName))  
       {  
         client.PutBucket(new PutBucketRequest()  
         {  
           BucketName = bucketName,  
           UseClientRegion = true //Utiliza a mesma região que estiver como padrão no WS.  
         });  
       }  
     }  

Outra propriedade importante também seria "CannedACL" que indica o tipo de permissão que o arquivo terá no storage da amazon aws, nesse caso eu coloquei como "S3CannedACL.PublicRead" que esse arquivo poderá ser visto publicamente.
Para concluirmos você deve ter reparado que ao fazer a chamada da classe "AWSClientFactory" você precisa passar a ela a sua chave e também a chave secreta. Essas duas chaves você pode gerar através do painel de controle da Amazon AWS.
Ao acessar o console https://aws.amazon.com/ com o teu login, você vai ver no menu acima na aba escrita com seu nome a opção "Security Credencials".

Acesse:

Agora é só gerar a sua key e secret key e inserir na chamada.
Se obtiver sucesso no upload você pode acessar o console na opção de serviço S3, você vai perceber que o seu "BucketName" e dentro dele o arquivo enviado, ao clicar sobre o arquivo perceba nas propriedade que Amazon AWS gerou uma url publica de acesso ao seu arquivo que será algo parecido com isso: https://s3.amazonaws.com/MeusArquivos/Arquivos/nome_arquivo
Agora é só usufruir da performance que a storage vai te oferecer além disso com um valor muito pequeno.

Espero ter ajudado.
Abraço,

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,