EF Core – Número sequencial customizável.

Criar um número sequencial é simples, mas e se ele tiver que começar de um determinado valor? E se precisar que seja incrementado de outra maneira? Aprenda como fazer!

Utilizar o EF Core facilita muito o processo de desenvolvimento, vamos aprender a configurar uma sequência customizada para evitar configurações extras no BD e deixar mais esta responsabilidade com nossa aplicação.

Neste exemplo vamos supor que possuímos uma classe Pedido e que possui uma propriedade código. Em código nós queremos que esse número inicie em 1000 no banco de dados e que seja incrementado automaticamente de 1 em 1.

O códigos a seguir explicam onde e como fazer esta simples configuração.

Primeiramente configuramos o contexto para criar uma sequencia chamada “MinhaSequencia” que inicia em 1000 e incrementa de 1 em 1.

Na configuração do mapeamento da entidade no EF basta apontar que aquela propriedade vai utilizar uma sequence conhecida:

Basta agora criar a Migration e rodar um “update-database” que estará tudo funcionando perfeitamente.


Vamos continuar a troca de experiências, deixe seu comentário abaixo. Se gostou e concorda com o artigo, compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis.

10 Boas práticas no desenvolvimento com ASP.NET Core

O ASP.NET Core trouxe uma série de novidades e mudanças em relação ao ASP.NET Clássico e essas novidades vieram para facilitar nossa vida no desenvolvimento, portanto são essenciais para qualquer desenvolvedor.

O conteúdo deste material está em vídeo, pois foi uma palestra online que realizei no Canal .NET e foi gravada ao vivo.

10BoasPraticasAspNetCore

Por que selecionei estas 10 boas práticas? Eu poderia selecionar 50 ou mais, porém estas 10 são essenciais para fazer o bom uso do ASP.NET Core portanto assista, pratique e implemente em seus projetos, fará um grande diferencial.

Tanto o PPT quando o código fonte estão disponíveis em meu GitHub.

Espero que vocês aproveitem as dicas e qualquer dúvida estou a disposição.

* Assine meu canal no Youtube ?


Caso esteja interessado em conhecer mais sobre Testes, TDD, ASP.NET, DDD, Padrões de Arquitetura e boas práticas de desenvolvimento não deixe de conferir as ementas dos meus cursos:

Vamos continuar a troca de experiências, deixe seu comentário abaixo. Se gostou e concorda com o artigo, compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis.

Turbinando sua aplicação ASP.NET no IIS

Toda aplicação ASP.NET requer um processo de compilação em memória no servidor e isto leva um tempo perceptível por nossos usuários. Aprenda como evitar esse comportamento.

Turbinando sua aplicação ASP.NET no IIS

Na minha vida de consultor eu sempre ouço a mesma pergunta dos meus clientes:
Meu site fica lento na primeira chamada toda vez que faço um deploy ou quando o pool do IIS recicla naturalmente.

Antes de tudo é importante deixar claro que não tem como fugir do processo de reciclagem do pool do IIS, ele pode ocorrer naturalmente por N fatores ou agendado conforme sua configuração.

A demora natural na primeira chamada de uma aplicação ASP.NET é o processo do IIS compilando o assembly da sua aplicação e disponibilizando ele num cache, esse cache é usado nas próximas chamadas e assim sua aplicação volta a responder normalmente sem nenhuma demora aparente. Toda vez que o pool do IIS recicla a memória esse cache é perdido.

Nós não queremos que nossos usuários sejam obrigados a esperar este tempo de compilação que pode chegar em cerca de 30 segundos dependendo do tamanho da aplicação, é por isso que o IIS possui uma feature pouco utilizada chamada de
IIS Auto-Start ou Application Initialization

Para habilitar esse comportamento no seu IIS é necessário instalar o módulo de Application Initialization no seu servidor e seguir alguns passos na configuração do IIS.

*A mesma configuração pode ser feita através da edição dos arquivos de configuração no servidor. Eu prefiro fazer no IIS diretamente, você encontrará esta outra abordagem no link de referência no final do post.

O resultado é muito satisfatório, ao invés da aplicação demorar mais de 10 segundos para responder ela passa a demorar 1 ou 2 segundos.

Para abordar todos os passos necessários em detalhes eu gravei um vídeo de 16 minutos mostrando todo o processo de configuração.

* Assine meu canal no Youtube 🙂

Referências


Se você estiver interessado em conhecer mais e aprender como desenvolver aplicações com uma arquitetura responsável utilizando DDD, TDD, BDD, aplicando os princípios SOLID, diversos Design Patterns e escrevendo testes de unidade conheça meus cursos:

Vamos continuar a troca de experiências, deixe seu comentário abaixo, se gostou e concorda com o artigo compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis. ;)

Ordene manualmente os seus scripts via Bundles

A feature Bundles e Minification foi introduzida a partir da versão do ASP.NET MVC 4 e trouxe bastante praticidade, porém precisamos de um ajuste manual para obter a ordenação exata dos scripts.

Utilizar os Bundles da forma tradicional pode ocasionar alguns problemas na execução de nossos scripts devido ao seu padrão de ordenação. Encontrar a solução para estes problemas pode tomar algumas horas de trabalho se não observarmos corretamente o comportamento dos Bundles.

Um exemplo simples:
Para utilizar a biblioteca de globalização de validações “jquery.validate.globalize.js” certamente precisaremos da biblioteca “jquery.validate.js” e “globalize.js”, pois são dependências para que a validação funcione de forma globalizada. Portanto para o browser poder executar os métodos de “jquery.validate.globalize.js” é necessário ter carregado previamente a bibliotecas que são dependências.

Utilizando os Bundles de forma tradicional teríamos uma configuração parecida com o exemplo a seguir.

// Adicionando jQuery
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
            "~/Scripts/jquery-{version}.js"));

// Adicionando Validação, e Globalização
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
            "~/Scripts/jquery.validate.js",
            "~/Scripts/globalize.js",
            "~/Scripts/jquery.validate.globalize.js"));

E por consequência o código HTML gerado para interpretação do browser não seria o esperado.

Bundles Uncaught ReferenceError

Uncaught ReferenceError: Globalize is not defined

Este problema ocorreu por que o Bundle não gerou os scripts na ordem em que foram declarados como no código acima, pois sua ordenação padrão não respeita a ordem da declaração. Logo a biblioteca “jquery.validate.globalize.js” não encontrou sua dependência e não executou suas funções.

Existem algumas formas de resolver este problema, minha recomendação é criar uma classe de ordenação que respeita a ordem em que as bibliotecas foram declaradas. Esta classe pode ser criada no próprio arquivo “BundleConfig.cs” para manter a simplicidade da implementação.

public class AsIsBundleOrderer : IBundleOrderer
{
    public IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile> files)
    {
        return files;
    }
}

E a forma de declaração do Bundle precisa ser modificada para fazer uso desta nova ordenação.

// Adicionando jQuery
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
            "~/Scripts/jquery-{version}.js"));

// Adicionando Validação, e Globalização
// Utilizando ordenação manual
var valBundle = new ScriptBundle("~/bundles/jqueryval").Include(
    "~/Scripts/jquery.validate.js",
    "~/Scripts/globalize.js",
    "~/Scripts/jquery.validate.globalize.js");

valBundle.Orderer = new AsIsBundleOrderer();

bundles.Add(valBundle);

Utilizamos algumas linhas a mais, porém garantimos a ordenação da forma que o browser precisa para poder executar tudo corretamente. O resultado é como o esperado.

Bundles Order Ok

Como podemos conferir é muito importante estar atento a este comportamento dos Bundles para evitar possíveis problemas e por consequência perder um tempo precioso de trabalho.

* Esta abordagem vale também para os StyleBundles que trabalham com CSS.

** Caso esteja trabalhando com o recurso de Minification este comportamento pode ocorrer ou não dependendo do cenário.


Se você estiver interessado em conhecer mais e aprender como desenvolver aplicações Web com arquitetura baseada em DDD, aplicando os princípios SOLID, diversos Design Patterns e escrevendo testes de unidade inscreva-se em meu curso:

Vamos continuar a troca de experiências, deixe seu comentário abaixo, se gostou e concorda com o artigo compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis. ;)

Desacoplando o ASP.NET Identity do MVC e Domínio

O ASP.NET Identity é uma ótima solução para a gestão dos usuários em sua aplicação, porém seu design é diretamente acoplado ao ASP.NET MVC e também devemos evitar de utilizar suas referências no domínio da aplicação.

Desacoplando o ASP.NET Identity. Finalmente após muitos pedidos eu consegui colocar a mão na massa e desenvolver um projeto modelo para ser usado como referência.

Desacoplando o ASP.NET Identity do MVC e Domínio

Como sabemos o ASP.NET Identity depende diretamente do EF (ou outros), Owin e System.Web, logo todas as implementações do ASP.NET Identity ficam de alguma forma acopladas em nossa camada de apresentação. Quando possuímos uma aplicação onde o usuário é uma peça importante no negócio existe a necessidade de representá-lo na camada de domínio, porém é altamente recomendado que a camada de domínio não conheça tecnologias terceiras (Identity, EF, outros).

Como resolver esse problema?

Foi após alguns estudos que cheguei no modelo que considero ser a “melhor abstração possível”, pois para o ASP.NET Identity funcionar na camada de apresentação é obrigatório levar algumas dependências, porém desde que esteja isolado em outra camada já conseguiremos ótimas boas vantagens.

Aviso: Caso você não conheça o ASP.NET Identity, DDD e Injeção de dependência sugiro que comece pelos tutoriais:

Neste tutorial você encontrará as seguintes tecnologias utilizadas

  • ASP.NET MVC 5
  • ASP.NET Identity 2
  • Entity Framework 6
  • Fluent API
  • Injeção de Dependência com Simple Injector
  • Owin
  • RestSharp para implementação do Twilio API (SMS)

Existem dois grandes objetivos nesta proposta, o primeiro objetivo é a reutilização, pois com um uma única solução (camada) de gestão de usuários é possível atender N aplicações, facilitando o gerenciamento e manutenção.

O segundo objetivo é permitir que a camada de domínio represente o usuário de forma abstraída, logo não existe referência direta do IdentityUser no domínio, porém é possível consultar os dados de usuário e incluir / modificar as informações salvas.

O primeiro passo é abstrair tudo, mover as dependências do Identity para uma camada isolada, depois injetar no MVC os objetos necessários, para essa implementação eu utilizei o Simple Injector, que como o próprio nome diz, é bem simples, rápido e atendeu bem a necessidade.

Com o Identity isolado e injetado devidamente na camada de apresentação (MVC) basta criar uma camada de acesso a dados que irá fornecer meios do domínio consumir dados do usuário de forma abstraída. A grande sacada está no uso correto do Fluent API e claro muita injeção de dependência.

A solução desde modelo não visa atender completamente a filosofia do DDD, pois procurei simplificar o bastante para deixar simples de modificar, por exemplo adicionando uma camada de Application. Portanto quem utilizar esta arquitetura deve estar ciente que algumas coisas a mais ainda precisam ser feitas.

Gostaria de ressaltar também que o template de projeto ASP.NET MVC que o Visual Studio fornece possui vários problemas de design de código. As controllers de Account e Manage do usuário precisaram ser parcialmente re-escritas para atender a injeção do Identity, problemas como mais de um construtor público e utilização do pattern (ou anti pattern?) Service Locator foram encontrados e retirados.

Para quem quiser acompanhar em detalhes técnicos toda a implementação dessa solução eu preparei o vídeo a seguir.

* Assine meu canal no Youtube 🙂

O código fonte desta solução está disponível no GitHub, caso queira propor alguma melhoria faça um Pull Request, será muito bem recebido.


Se você estiver interessado em conhecer mais e aprender como desenvolver aplicações Web com arquitetura baseada em DDD, aplicando os princípios SOLID, diversos Design Patterns e escrevendo testes de unidade inscreva-se em meu curso:

Vamos continuar a troca de experiências, deixe seu comentário abaixo, se gostou e concorda com o artigo compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis. ;)

Tutorial ASP.NET MVC 5 + DDD + EF + AutoMapper + IoC + Dicas e Truques

Arquitetura de Sistemas Corporativos é um tema muito menos explorado do que ASP.NET MVC 5 e nem por isso é menos importante (na verdade é bem mais), neste vídeo tutorial eu mostrarei como criar uma arquitetura padrão DDD utilizando ASP.NET MVC 5.2, Entity Framework, AutoMapper, IoC com Ninject e muitas dicas para criar uma arquitetura modelo e totalmente responsável.

ASP.NET MVC 5 + DDD + EF + AutoMapper + IoC

Como poderão acompanhar nos slides e no vídeo ASP.NET MVC é apenas a ponta do iceberg em uma aplicação corporativa. Quando entramos no mundo real os exemplos dos artigos de sites e livros não nos atendem mais e é necessário buscar mais conhecimento para criar uma aplicação robusta, responsável, testável, escalável e de fácil manutenção.

O modelo DDD (Domain Driven Design) atende muito bem cenários de aplicações corporativas e eu utilizo muito em meus projetos profissionais e pessoais.

Neste vídeo tutorial você aprenderá

  • Criar uma solução padrão DDD
  • Separar a aplicação em camadas
  • Entidades de Domínio
  • Classes de Serviço
  • Criar Contratos (Interfaces)
  • Repositório Genérico
  • Repositório Especializado
  • Criar um Contexto do Entity Framework
  • Trabalhar com Migrations
  • Criar novas convenções do Entity Framework
  • Remover algumas convenções do Entity Framework
  • Sobrescrever o método SaveChanges para persistência de dados
  • Programar com CodeFirst
  • Utilizar FluentAPI para modelar tabelas
  • Criar Relacionamentos entre Entidades e refletindo nas tabelas do banco de dados.
  • Criar e utilizar a camada de Application
  • Trabalhar com classes genéricas de Entidades
  • Abstrair camadas com Injeção de Dependência (IoC)
  • Implementar o Ninject como container de IoC (DI)
  • Utilizar ViewModels
  • Utilizar DataAnnotations para validação de formulários
  • Mapear ViewModels x Entidade de Domínio com AutoMapper
  • Muitas dicas para acelerar sua produção

Este conteúdo é aplicado no meu curso de ASP.NET MVC 5 – Enterprise Applications com uma carga horária de 16 horas, com todo o embasamento teórico, técnico e prático, muitos outros patterns, testes, mocks, serviços REST, manipulação de filtros do ASP.NET MVC e etc são abordados no curso para uma preparação completa do futuro arquiteto desenvolvedor.

SLIDES

VÍDEO (3h00 de duração)

* Assine meu canal no Youtube 🙂

SOURCE

Para download do projeto clique aqui (logo será transferido para o GitHub)

Referências

Vamos continuar enriquecendo o assunto, poste aqui sua opinião ou dúvida 😉

Uma técnica simples para utilizar DropDownList no ASP.NET MVC

Utilizar o HtmlHelper para criar um DropDownList em uma View do ASP.NET MVC pode ser um desafio para iniciantes em MVC, veja como facilitar seu trabalho utilizando uma técnica simples.

Existem diversas formas de popular e controlar o estado de um DropDownList gerado pelo HtmlHelper, algumas podem dar um certo trabalho e confundir o programador que está iniciando com ASP.NET MVC e ainda não dominou todas as técnicas.

Conforme o código a seguir, criamos na pasta Models uma classe chamada Cliente que será utilizada para popular um DropDownList, observe que foi criado um método que retorna uma lista de clientes pré definidos (apenas para facilitar a demonstração).

using System.Collections.Generic;

namespace DropDownList.Models
{
    public class Cliente
    {
        public int ClienteId { get; set; }
        public string Nome { get; set; }

        public List<Cliente> ListaClientes()
        {
            return new List<Cliente>
            {
                new Cliente { ClienteId = 1, Nome = "Eduardo Pires"},
                new Cliente { ClienteId = 2, Nome = "João Silva"},
                new Cliente { ClienteId = 3, Nome = "Fulano de Tal"}
            };
        }
    }
}

A classe de Controller possui dois ActionResults, um para Get e um para Post, no primeiro ActionResult é criada uma ViewBag utilizando um nome que facilite o entendimento (no caso o ClienteId).

Esta ViewBag recebe uma estrutura DropDownList que é criada através da classe SelectList e os parâmetros utilizados são: Source, Value, Name (Origem do dado [Método ListarClientes], valor do elemento e nome a ser exibido). Estes parâmetros precisam coincidir com as propriedades da estrutura do seu dado (classe Cliente).

using System.Web.Mvc;
using DropDownList.Models;

namespace DropDownList.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            // Criando uma ViewBag com uma lista de clientes.
            // Utilizo o nome da ViewBag com ClienteId apenas
            // para facilitar o entendimento do código
            // new SelectList significa retornar uma
            // estrutura de DropDownList
            ViewBag.ClienteId = new SelectList
                (
                    new Cliente().ListaClientes(),
                    "ClienteId",
                    "Nome"
                );

            return View();
        }

        [HttpPost]
        // No Post o valor selecionado do DropDownList
        // será recebido no parametro clienteId
        public ActionResult Index(string clienteId)
        {
            // O quarto parametro "clienteId" diz qual é o ID
            // que deve vir pré-selecionado ao montar o DropDownList
            ViewBag.ClienteId = new SelectList
                (
                    new Cliente().ListaClientes(),
                    "ClienteId",
                    "Nome",
                    clienteId
                );

            return View();
        }
    }
}

Ao observar o retorno da criação do SelectList no Quick Watch fica mais fácil de entender a estrutura retornada

DropDownList ASP.NET MVC

Para o Razor HtmlHelper criar corretamente seu DropDownList basta informar (no formato String) o nome da ViewBag que contém a estrutura de dados, foi adicionado um segundo parâmetro para criar um elemento em branco na primeira posição, assim evitando que o primeiro item seja selecionado por engano.

@{
    ViewBag.Title = "Teste DropDownList";
}

@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    <div>
        <h4>@ViewBag.Title</h4>
    </div>
    <div class="row">
        <div class="col-md-8">
            Escolha o cliente:
            @* Passando o Nome da ViewBag *@
            @Html.DropDownList("ClienteId", String.Empty)

            <input type="submit" value="Enviar" class="btn btn-default" />
        </div>
    </div>
}

O resultado ficará assim

DropDownList ASP.NET MVC

A View possui um Form que fará um post para a segunda ActionResult da Controller que já espera receber o valor do item selecionado no DropDownList, o mesmo processo é repetido só que no final da criação da estrutura da SelectList é repassado mais um parâmetro que é o valor do item que foi selecionado na tela anterior, esse parâmetro garante que o foco da seleção mantenha-se no item que foi previamente selecionado.

Com isso vimos que é muito simples utilizar o DropDownList utilizando esta técnica, caso queira treinar basta criar um projeto simples ASP.NET MVC e implementar o código disponibilizado aqui.

Caso desconheça o uso de ViewBag leia este meu artigo que explica os conceitos:
ASP.Net MVC – ViewData, ViewBag e TempData

Até a próxima! 😉

ASP.Net MVC – Autenticando usuário através da conta do Facebook

A utilização de contas de redes sociais para autenticação em outras aplicações já é uma realidade, aprenda como autenticar um usuário utilizando a conta do Facebook em sua aplicação ASP.Net MVC e faça parte desta nova tendência.

ASP.Net MVC + Facebook

O ASP.Net MVC em conjunto com o Visual Studio proporciona uma enorme facilidade em executar esta integração, um projeto no template Internet Application por exemplo já vem praticamente pronto para realizar essa integração com as seguintes contas:

  • Facebook
  • Twitter
  • Microsoft
  • Google

Sua aplicação ASP.Net MVC pode trabalhar com estes quatro modelos simultaneamente sem problema algum, o esquema de membership do ASP.Net proporciona o armazenamento e identificação de cada tipo de login e sua rede social de origem.

Neste artigo gravei um vídeo seguindo passo a passo como integrar uma aplicação ASP.Net MVC com o Facebook, porém este processo é muito similar a integração com as demais redes sociais disponíveis.

Neste vídeo não foi necessário utilizar nenhum plugin externo, porém faço menção ao Facebook SDK for .NET que é um SDK para trabalhar com recursos avançados de integração com o Facebook, como por exemplo postar informações na timeline e interagir com a lista de amigos.

A partir do momento em que o usuário está autenticado numa aplicação ASP.Net MVC utilizando a conta do Facebook o Facebook SDK complementa as demais funcionalidades possíveis para uma interatividade completa.

Obtenha o Facebook SDK for .NET no site:
http://facebooksdk.net/

Ou instale diretamente via NuGet

PM> Install-Package Facebook

Em um próximo vídeo exibirei como utilizar esse SDK e realizar integrações no Facebook além do login.

Espero que aproveite o vídeo e deixe seu feedback ou dúvidas aqui nos comentários.

ASP.Net MVC – Propriedade com o atributo DataType.Date não exibe valor na View

Propriedade na Model decorada com o DataAnnotation [DataType(DataType.Date)] ao ser criada na View através do @Html.EditorFor não exibe os dados gravados no banco.

Essa é uma dica rápida mas que pode lhe poupar algum tempo, o cenário abordado resulta em um problema muito comum de ocorrer e será detalhado aqui como solucionar.

Em uma classe Model possuímos uma propriedade que representa uma data, estamos utilizando DataAnnotation para definir alguns comportamentos.

public class MeuModelo
{
    [Display(Name = "Data de Retorno")]
    [DataType(DataType.Date, ErrorMessage="Data em formato inválido")]
    public DateTime? DataRetorno { get; set; }
}

Note que está sendo utilizado o atributo [DataType(DataType.Date)] para definir a forma que este dado será validado na View.

Por tratar-se de uma solução ASP.Net MVC, muito provavelmente será utilizado o HtmlHelper (Razor) para facilitar a criação do controle na View.

    <div>
        @Html.LabelFor(model => model.DataRetorno)
        @Html.EditorFor(model => model.DataRetorno)
        @Html.ValidationMessageFor(model => model.DataRetorno)
    </div>

Ao chamar a View teremos uma surpresa, o campo além de estar criado, tornou-se um DatePicker, fantástico não?

ASP.Net MVC DatePicker

Esse é um recurso do ASP.Net MVC para browsers que suportam HTML5, logo se o seu browser possui esse suporte você terá acesso a este recurso.

Até neste momento tudo está funcionando perfeitamente até que chega o momento em que este campo data irá exibir um dado já gravado (ex banco de dados).
A data não aparece, o campo continua tendo o mesmo aspecto, como se não tivesse sido preenchido.

Você debuga o código, valida o preenchimento da model e o dado está la populado. Que problema é esse? É neste momento que entra a dica:

Para exibir corretamente a data, o valor deve ser formatado como AAAA-MM-DD

[RFC 3339] definida pelo W3C diz o seguinte:

“A valid full-date as defined in [RFC 3339], with the additional qualification that the year component is four or more digits representing a number greater than 0.”

Ou seja, é necessário formatar este campo data para adequar o seu funcionamento.
Caso a página estiver sendo exibida em um browser sem suporte a HTML5 o problema não ocorrerá, porém de qualquer forma necessita ser tratado, afinal todos os browsers atuais possuem esse suporte.

Para corrigir o problema será usado o atributo DisplayFormat, uma classe do DataAnnotations que especifica como que um dado dinamicamente criado deve ser exibido.

public class MeuModelo
{
    [Display(Name = "Data de Retorno")]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
    [DataType(DataType.Date, ErrorMessage="Data em formato inválido")]
    public DateTime? DataRetorno { get; set; }
}

O formato da composição da string deve ser exatamente como está exibido no exemplo.
Ao testar a View percebemos o resultado, funcionou e agora está exibindo o dado gravado.

ASP.Net MVC DatePicker

Caso você tenha problema com o formato da exibição da data, talvez seja necessário utilizar os scripts jQuery Globalize para adequação do formato para a cultura do idioma da página.

Espero que essa dica possa ajudar, qualquer dúvida poste nos comentários abaixo 😉

ASP.Net MVC – Cuidado com links de exclusão “Delete” – Pode ser uma falha de segurança

ASP.Net MVC – Ao expor informações de um banco de dados, cuidado com o link do tipo “Delete”, siga essa dica para evitar uma falha de segurança em seu site.

Vamos supor que em nosso site ASP.Net MVC possuímos uma controller “FuncionarioController”, essa controller é responsável pelos métodos de CRUD da sua entidade Funcionario relacionada ao seu banco de dados.

Como o seu ActionResult de exclusão deveria estar escrito? Vamos observar este exemplo:

// GET: /Funcionario/Delete/5

public ActionResult Delete(int id = 0)
{
   Funcionario funcionario = db.Funcionario.Find(id);

   if (funcionario == null)
   {
      return HttpNotFound();
   }

   db.Funcionario.Remove(funcionario);
   db.SaveChanges();

   return RedirectToAction("Index");
}

Algo de estranho?
Teoricamente está tudo certo, o código está validando se o ID existe mesmo e caso não exista o usuário será redirecionando para uma página de erro, assim evitando em exibir mensagens de erro do banco de dados.

Mas nesse código habita uma grande falha de segurança, o ActionResult Delete é um método Get, ou seja, pode ser chamado diretamente de uma URL. O que aconteceria se alguém mal intencionado digitasse no browser a seguinte URL:

www.meusite.com.br/Funcionario/Delete/1

Provavelmente o registro de funcionário com o ID = 1 seria excluído certo?
“Ah, mas minha aplicação requer login e senha para esse tipo de ação.”

Isso não evita de receber um ataque, imagine que um usuário mal intencionado deseja deletar um registro e envia um e-mail para um usuário desse sistema com uma imagem “para parecer inofensivo” e nessa imagem contenha um link para a URL que citamos acima. Se o usuário que recebeu o e-mail e clicou estiver conectado (logado) no sistema naquele momento já seria o suficiente para concretizar a exploração dessa falha.

DICA

Nunca escreva seus métodos Get de exclusão realizando a exclusão direta da base.
A dica completa seria:
Nunca escreva nenhum método Get realizando alteração de informações do sistema.

Como resolver?

Vamos observar uma maneira indicada para evitar a falha citada:

    // GET: /Funcionario/Delete/5

    public ActionResult Delete(int id = 0)
    {
        Funcionario funcionario = db.Funcionario.Find(id);

        if (funcionario == null)
        {
            return HttpNotFound();
        }
        return View(funcionario);
    }

    // POST: /Funcionario/Delete/5

    [HttpPost, ActionName("Delete")]
    public ActionResult DeleteConfirmed(int id)
    {
        Funcionario funcionario = db.Funcionario.Find(id);
        db.Funcionario.Remove(funcionario);
        db.SaveChanges();

        return RedirectToAction("Index");
    }

Repare que existem dois métodos ActionResult para exclusão, o método Get Delete não realiza a exclusão do registro, apenas verifica se ele existe e redireciona o usuário para uma View de visualização e confirmação de exclusão:

ASP.Net MVC Exclusão

Ao realizar a confirmação dos dados e clicar em excluir a página será submetida via Post para o método DeleteConfirmed, que é responsável pela exclusão da base de dados.

Repare no código que um método chama-se Delete (Get) e o outro DeleteConfirmed (Post), repare também que existe um atributo ActionName(“Delete”) decorando o método DeleteConfirmed, isso significa que ele pode ser chamado como Delete, isso é necessário, pois o CLR (common language runtime) do .Net requer que haja apenas um método com a mesma assinatura, como os dois métodos recebem o mesmo tipo de parâmetro não seria possível possuírem o mesmo nome.

Utilizando o atributo ActionName(“Delete”)  faz que o roteamento do site aceite o método DeleteConfirmed como Delete quando uma URL que inclua o /Delete/ for acionada via Post.

Resumo

Essa técnica evita que de forma indesejável um registro seja manipulado e por mais que haja sucesso na tentativa do usuário mal intencionado o site resultará em uma página de confirmação, logo o usuário logado poderá intervir a essa tentativa de burlar o sistema.

Todas as controllers criadas automaticamente em projetos no Visual Studio, seja na criação do projeto ou via scaffolding já preveem essa técnica, caso você esteja criando suas controllers manualmente, lembre-se dessa dica 😉

Referência

Gostou do artigo, teve alguma dúvida? Deixe sua mensagem nos comentários abaixo.