ASP.NET MVC – ModelBinder para datas brasileiras

Olá meu caro leitor(a).

Nesse post venho trazer um problema que tivemos em um dos projetos ASP.NET MVC 5 que desenvolvemos e também demonstrar a solução para este problema que pode ser recorrente para muitos.

Sobre o problema

Temos um formulário que possui dois campos que irão receber dados no formato de data. Quando realizamos o submit desse formulário para o nosso server com o valor “31/01/2017” por exemplo, quando o ASP.NET MVC realiza o bind dos valores sempre é demonstrado o valor da propriedade do nosso objeto com o valor DateTime.MinValue, pelo fato do ModelBinder do ASP.NET MVC não conseguia realizar o parse do dado usando as configurações regionais (cultura) da máquina.

A solução

Para resolver esse problema, foi criado um ModelBinder customizado para o tipo “DateTime” que terá a responsabilidade de efetuar o parse da data para o formato brasileiro.

Abaixo está o código do binder customizado:

using System;
using System.Globalization;
using System.Web.Mvc;

namespace CustomModelBinder
{
    /// <summary>
    /// Classe que irá realizar o bind dos dados para os campos do tipo data no formato "PtBr".
    /// </summary>
    public class BrazilianDateTimeModelBinder : IModelBinder
    {
        /// <summary>
        /// Realiza o parse do valor para a data no formato "pt-BR".
        /// </summary>
        /// <param name="controllerContext">Objeto do contexto do controller.</param>
        /// <param name="bindingContext">Objeto do contexto do Model binding.</param>
        /// <returns>Retorna o valor do dado realizado o parse.</returns>
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (controllerContext == null)
            {
                throw new ArgumentNullException(nameof(controllerContext));
            }

            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            if (string.IsNullOrEmpty(valueProviderResult.AttemptedValue))
            {
                return valueProviderResult.AttemptedValue;
            }

            // Tenta converter a data para formato brasileiro.
            DateTime parsedDate;
            var parsed = DateTime.TryParse(valueProviderResult.AttemptedValue, CultureInfo.GetCultureInfo("pt-BR").DateTimeFormat, DateTimeStyles.None, out parsedDate);

            // Se não consegue realizar o parse da data será adicionado um erro no ModelState.
            if (!parsed)
            {
                bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Data inválida");
            }

            return parsedDate;
        }
    }
}

Com a criação dessa classe devemos adicionar no dicionários de “binders” do ASP.NET MVC qual é o tipo específico que deve utilizar esse binder customizado para realizar o parse dos dado.

Geralmente utilizo a abordagem de criar uma classe estática de configuração na pasta “App_Start” da aplicação que irá conter um método estático para registrar os binders customizados. Mas caso não queira seguir essa abordagem, pode informar os dados das linhas 16 e 17 no seu arquivo Global.asax:

using System;
using System.Web.Mvc;

namespace CustomModelBinder
{
    /// <summary>
    /// Arquivo de configuração para registrar os "ModelBinder" customizados.
    /// </summary>
    public static class ModelBinderConfig
    {
        ///<summary>
        /// Registra os "ModelBinder" customizados.
        /// </summary>
        public static void RegisterModelBinders()
        {
            ModelBinders.Binders.Add(typeof(DateTime), new BrazilianDateTimeModelBinder());
            ModelBinders.Binders.Add(typeof(DateTime?), new BrazilianDateTimeModelBinder());
        }
    }
}

Notem que estamos registrando o ModelBinder customizado para os tipos “DateTime” e “DateTime?” (objetos do tipo DateTime que podem ser nulos).

Com a classe de configuração criada, basta informar no arquivo Global.asax a chamada para o método “RegisterModelBinders“:

namespace CustomModelBinder
{
    public class MvcApplication : HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            // Registra os binders para dos dados das requisições.
            ModelBinderConfig.RegisterModelBinders();
        }
    }
}

Conclusão

Como visto, o problema foi resolvido usando um dos recursos que o ASP NET MVC nós proporciona e essa abordagem nos dá liberdade de criarmos os nossos próprios binders, assim tratando os dados conforme necessitamos.

Caso tenha interesse em criar um binder para realizar o parse de valores monetários, sugiro o post “Model Binding Decimal Values” que demonstra essa abordagem.

Espero que tenham gostado do post e também espero essa solução ajudem outros a solucionarem esse problema de data no formato brasileiro.

Obrigado pela leitura e até o próximo post.

Legolas

Anúncios