Compartilhar via


Parte 9, adicionar a validação a um aplicativo ASP.NET Core MVC

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.

Aviso

Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para a versão atual, consulte a versão .NET 9 deste artigo.

De Rick Anderson

Nesta seção:

  • A lógica de validação é adicionada ao modelo Movie.
  • Você garante que as regras de validação sejam impostas sempre que um usuário criar ou editar um filme.

Mantendo o processo DRY

Um dos princípios de design do MVC é o DRY (“Don't Repeat Yourself”). O ASP.NET Core MVC incentiva você a especificar a funcionalidade ou o comportamento somente uma vez e, em seguida, refleti-lo em qualquer lugar de um aplicativo. Isso reduz a quantidade de código que você precisa escrever e faz com que o código escrito seja menos propenso a erros e mais fácil de testar e manter.

O suporte de validação fornecido pelo MVC e pelo Entity Framework Core é um bom exemplo do princípio DRY em ação. Especifique as regras de validação de forma declarativa em um único lugar (na classe de modelo) e as regras são impostas em qualquer lugar no aplicativo.

Excluir os dados editados anteriormente

Na próxima etapa, são adicionadas regras de validação que não permitem valores nulos. Execute o aplicativo, navegue até /Movies/Index, exclua todos os filmes listados e interrompa o aplicativo. O aplicativo usará os dados de semente na próxima vez que for executado.

Adicionar regras de validação ao modelo de filme

O namespace DataAnnotations fornece um conjunto de atributos de validação internos aplicados de forma declarativa a uma classe ou propriedade. DataAnnotations também contém atributos de formatação como DataType, que ajudam com a formatação e não fornecem validação.

Atualize a classe Movie para aproveitar os atributos de validação internos Required, StringLength, RegularExpression e Range o atributo de formatação DataType.

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }

    [StringLength(60, MinimumLength = 3)]
    [Required]
    public string? Title { get; set; }

    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }    

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
    [Required]
    [StringLength(30)]
    public string? Genre { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
    [StringLength(5)]
    [Required]
    public string? Rating { get; set; }
}

Os atributos de validação especificam o comportamento que você deseja impor nas propriedades de modelo às quais eles são aplicados:

  • Os atributos Required e MinimumLength indicam que uma propriedade deve ter um valor; porém, nada impede que um usuário insira um espaço em branco para atender a essa validação.

  • O atributo RegularExpression é usado para limitar quais caracteres podem ser inseridos. No código anterior, "Gênero":

    • Deve usar apenas letras.
    • A primeira letra deve ser maiúscula. Espaços em branco são permitidos, enquanto números e caracteres especiais não são.
  • A "Classificação" RegularExpression:

    • Exige que o primeiro caractere seja uma letra maiúscula.
    • Permite caracteres especiais e números nos espaços subsequentes. "PG-13" é válido para uma classificação, mas é um erro em "Gênero".
  • O atributo Range restringe um valor a um intervalo especificado.

  • O atributo StringLength permite definir o tamanho máximo de uma propriedade de cadeia de caracteres e, opcionalmente, seu tamanho mínimo.

  • Os tipos de valor (como decimal, int, float, DateTime) são inerentemente necessários e não precisam do atributo [Required].

Ter as regras de validação automaticamente impostas pelo ASP.NET Core ajuda a tornar seu aplicativo mais robusto. Também garante que você não se esqueça de validar algo e inadvertidamente permita dados incorretos no banco de dados.

Interface do usuário de erro de validação

Execute o aplicativo e navegue para o controlador Movies.

Selecione o link Criar Novo para adicionar um novo filme. Preencha o formulário com alguns valores inválidos. Assim que a validação do lado do cliente do jQuery detecta o erro, ela exibe uma mensagem de erro.

Formulário da exibição de filmes com vários erros de validação do lado do cliente do jQuery

Observação

Talvez você não consiga inserir vírgulas decimais em campos decimais. Para dar suporte à validação do jQuery para localidades de idiomas diferentes do inglês que usam uma vírgula (“,”) para um ponto decimal e formatos de data diferentes do inglês dos EUA, você deve tomar medidas para globalizar o aplicativo. Confira o comentário 4076 do GitHub para obter instruções sobre como adicionar casas decimais.

Observe como o formulário renderizou automaticamente uma mensagem de erro de validação apropriada em cada campo que contém um valor inválido. Os erros são impostos no lado do cliente (usando o JavaScript e o jQuery) e no lado do servidor (caso um usuário tenha o JavaScript desabilitado).

Uma vantagem significativa é que você não precisa alterar uma única linha de código na classe MoviesController ou na exibição Create.cshtml para habilitar essa interface do usuário de validação. O controlador e as exibições criados anteriormente neste tutorial selecionaram automaticamente as regras de validação especificadas com atributos de validação nas propriedades da classe de modelo Movie. Teste a validação usando o método de ação Edit e a mesma validação é aplicada.

Os dados de formulário não serão enviados para o servidor enquanto houver erros de validação do lado do cliente. Verifique isso colocando um ponto de interrupção no método HTTP Post usando a ferramenta Fiddler ou as ferramentas do Desenvolvedor F12.

Como funciona a validação

Talvez você esteja se perguntando como a interface do usuário de validação foi gerada sem atualizações do código no controlador ou nas exibições. O código a seguir mostra os dois métodos Create.

// GET: Movies/Create
public IActionResult Create()
{
    return View();
}

// POST: Movies/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (ModelState.IsValid)
    {
        _context.Add(movie);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O primeiro método de ação (HTTP GET) Create exibe o formulário Criar inicial. A segunda versão ([HttpPost]) manipula a postagem de formulário. O segundo método Create (a versão [HttpPost]) chama ModelState.IsValid para verificar se o filme tem erros de validação. A chamada a esse método avalia os atributos de validação que foram aplicados ao objeto. Se o objeto tiver erros de validação, o método Create exibirá o formulário novamente. Se não houver erros, o método salvará o novo filme no banco de dados. Em nosso exemplo de filme, o formulário não é postado no servidor quando há erros de validação detectados no lado do cliente; o segundo método Create nunca é chamado quando há erros de validação do lado do cliente. Se você desabilitar o JavaScript no navegador, a validação do cliente será desabilitada e você poderá testar o método Create HTTP POST ModelState.IsValid detectando erros de validação.

Defina um ponto de interrupção no método [HttpPost] Create e verifique se o método nunca é chamado; a validação do lado do cliente não enviará os dados de formulário quando forem detectados erros de validação. Se você desabilitar o JavaScript no navegador e, em seguida, enviar o formulário com erros, o ponto de interrupção será atingido. Você ainda pode obter uma validação completa sem o JavaScript.

A imagem a seguir mostra como desabilitar o JavaScript no navegador Firefox.

Firefox: Na guia Conteúdo de Opções, desmarque a caixa de seleção Habilitar JavaScript.

A imagem a seguir mostra como desabilitar o JavaScript no navegador Chrome.

Google Chrome: Na seção JavaScript das configurações de Conteúdo, selecione Não permitir que nenhum site execute JavaScript.

Depois de desabilitar o JavaScript, poste os dados inválidos e execute o depurador em etapas.

Durante a depuração em uma postagem de dados inválidos, o IntelliSense em ModelState.IsValid mostra que o valor é falso.

A parte do modo de exibição Create.cshtml é mostrada na seguinte marcação:

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>

            @*Markup removed for brevity.*@

a marcação anterior é usada pelos métodos de ação para exibir o formulário inicial e exibi-lo novamente em caso de erro.

O Auxiliar de Marcação de Entrada usa os atributos de DataAnnotations e produz os atributos HTML necessários para a Validação do jQuery no lado do cliente. O Auxiliar de Marcação de Validação exibe erros de validação. Consulte Validação para obter mais informações.

O que é realmente interessante nessa abordagem é que o controlador nem o modelo de exibição Create sabem nada sobre as regras de validação reais que estão sendo impostas ou as mensagens de erro específicas exibidas. As regras de validação e as cadeias de caracteres de erro são especificadas somente na classe Movie. Essas mesmas regras de validação são aplicadas automaticamente à exibição Edit e a outros modelos de exibição que podem ser criados e que editam o modelo.

Quando você precisar alterar a lógica de validação, faça isso exatamente em um lugar, adicionando atributos de validação ao modelo (neste exemplo, a classe Movie). Você não precisa se preocupar se diferentes partes do aplicativo estão inconsistentes com a forma como as regras são impostas – toda a lógica de validação será definida em um lugar e usada em todos os lugares. Isso mantém o código muito limpo e torna-o mais fácil de manter e desenvolver. Além disso, isso significa que você respeitará totalmente o princípio DRY.

Usando atributos DataType

Abra o arquivo Movie.cs e examine a classe Movie. O namespace System.ComponentModel.DataAnnotations fornece atributos de formatação, além do conjunto interno de atributos de validação. Já aplicamos um valor de enumeração DataType à data de lançamento e aos campos de preço. O código a seguir mostra as propriedades ReleaseDate e Price com o atributo DataType apropriado.

[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }

[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }    

Os atributos DataType fornecem dicas apenas para que o mecanismo de exibição formate os dados e fornece elementos/atributos como <a> para as URLs e <a href="mailto:EmailAddress.com"> para o email. Use o atributo RegularExpression para validar o formato dos dados. O atributo DataType é usado para especificar um tipo de dados mais específico do que o tipo intrínseco de banco de dados; eles não são atributos de validação. Nesse caso, apenas desejamos acompanhar a data, não a hora. A Enumeração DataType fornece muitos tipos de dados, como Date, Time, PhoneNumber, Currency, EmailAddress e muito mais. O atributo DataType também pode permitir que o aplicativo forneça automaticamente recursos específicos a um tipo. Por exemplo, um link mailto: pode ser criado para DataType.EmailAddress e um seletor de data pode ser fornecido para DataType.Date em navegadores que dão suporte a HTML5. Os atributos DataType emitem atributos data- HTML 5 (ou "data dash") que são reconhecidos pelos navegadores HTML 5. Os atributos DataTypenão fornecem nenhuma validação.

DataType.Date não especifica o formato da data exibida. Por padrão, o campo de dados é exibido de acordo com os formatos padrão com base nas CultureInfo do servidor.

O atributo DisplayFormat é usado para especificar explicitamente o formato de data:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

A configuração ApplyFormatInEditMode especifica que a formatação também deve ser aplicada quando o valor é exibido em uma caixa de texto para edição. (Talvez você não deseje isso em alguns campos – por exemplo, para valores de moeda, provavelmente, você não deseja exibir o símbolo de moeda na caixa de texto para edição.)

Você pode usar o atributo DisplayFormat por si só, mas geralmente é uma boa ideia usar o atributo DataType. O atributo DataType transmite a semântica dos dados, ao invés de apresentar como renderizá-lo em uma tela e oferece os seguintes benefícios que você não obtém com DisplayFormat:

  • O navegador pode habilitar os recursos do HTML5 (por exemplo, mostrar um controle de calendário, o símbolo de moeda apropriado à localidade, links de email, etc.)

  • Por padrão, o navegador renderizará os dados usando o formato correto de acordo com a localidade.

  • O atributo DataType pode permitir que o MVC escolha o modelo de campo correto para renderizar os dados (o DisplayFormat, se usado por si só, usa o modelo de cadeia de caracteres).

Observação

A validação do jQuery não funciona com os atributos Range e DateTime. Por exemplo, o seguinte código sempre exibirá um erro de validação do lado do cliente, mesmo quando a data estiver no intervalo especificado:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

Você precisará desabilitar a validação de data do jQuery para usar o atributo Range com DateTime. Geralmente, não é uma boa prática compilar datas rígidas nos modelos e, portanto, o uso do atributo Range e de DateTime não é recomendado.

O seguinte código mostra como combinar atributos em uma linha:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; }
    [Display(Name = "Release Date"), DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$"), Required, StringLength(30)]
    public string Genre { get; set; }
    [Range(1, 100), DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }
    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$"), StringLength(5)]
    public string Rating { get; set; }
}

Na próxima parte da série, examinaremos o aplicativo e faremos algumas melhorias nos métodos Details e Delete gerados automaticamente.

Recursos adicionais

Nesta seção:

  • A lógica de validação é adicionada ao modelo Movie.
  • Você garante que as regras de validação sejam impostas sempre que um usuário criar ou editar um filme.

Mantendo o processo DRY

Um dos princípios de design do MVC é o DRY (“Don't Repeat Yourself”). O ASP.NET Core MVC incentiva você a especificar a funcionalidade ou o comportamento somente uma vez e, em seguida, refleti-lo em qualquer lugar de um aplicativo. Isso reduz a quantidade de código que você precisa escrever e faz com que o código escrito seja menos propenso a erros e mais fácil de testar e manter.

O suporte de validação fornecido pelo MVC e pelo Entity Framework Core Code First é um bom exemplo do princípio DRY em ação. Especifique as regras de validação de forma declarativa em um único lugar (na classe de modelo) e as regras são impostas em qualquer lugar no aplicativo.

Excluir os dados editados anteriormente

Na próxima etapa, são adicionadas regras de validação que não permitem valores nulos. Execute o aplicativo, navegue até /Movies/Index, exclua todos os filmes listados e interrompa o aplicativo. O aplicativo usará os dados de semente na próxima vez que for executado.

Adicionar regras de validação ao modelo de filme

O namespace DataAnnotations fornece um conjunto de atributos de validação internos aplicados de forma declarativa a uma classe ou propriedade. DataAnnotations também contém atributos de formatação como DataType, que ajudam com a formatação e não fornecem validação.

Atualize a classe Movie para aproveitar os atributos de validação internos Required, StringLength, RegularExpression e Range o atributo de formatação DataType.

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }

    [StringLength(60, MinimumLength = 3)]
    [Required]
    public string? Title { get; set; }

    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }    

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
    [Required]
    [StringLength(30)]
    public string? Genre { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
    [StringLength(5)]
    [Required]
    public string? Rating { get; set; }
}

Os atributos de validação especificam o comportamento que você deseja impor nas propriedades de modelo às quais eles são aplicados:

  • Os atributos Required e MinimumLength indicam que uma propriedade deve ter um valor; porém, nada impede que um usuário insira um espaço em branco para atender a essa validação.

  • O atributo RegularExpression é usado para limitar quais caracteres podem ser inseridos. No código anterior, "Gênero":

    • Deve usar apenas letras.
    • A primeira letra deve ser maiúscula. Espaços em branco são permitidos, enquanto números e caracteres especiais não são.
  • A "Classificação" RegularExpression:

    • Exige que o primeiro caractere seja uma letra maiúscula.
    • Permite caracteres especiais e números nos espaços subsequentes. "PG-13" é válido para uma classificação, mas é um erro em "Gênero".
  • O atributo Range restringe um valor a um intervalo especificado.

  • O atributo StringLength permite definir o tamanho máximo de uma propriedade de cadeia de caracteres e, opcionalmente, seu tamanho mínimo.

  • Os tipos de valor (como decimal, int, float, DateTime) são inerentemente necessários e não precisam do atributo [Required].

Ter as regras de validação automaticamente impostas pelo ASP.NET Core ajuda a tornar seu aplicativo mais robusto. Também garante que você não se esqueça de validar algo e inadvertidamente permita dados incorretos no banco de dados.

Interface do usuário de erro de validação

Execute o aplicativo e navegue para o controlador Movies.

Selecione o link Criar Novo para adicionar um novo filme. Preencha o formulário com alguns valores inválidos. Assim que a validação do lado do cliente do jQuery detecta o erro, ela exibe uma mensagem de erro.

Formulário da exibição de filmes com vários erros de validação do lado do cliente do jQuery

Observação

Talvez você não consiga inserir vírgulas decimais em campos decimais. Para dar suporte à validação do jQuery para localidades de idiomas diferentes do inglês que usam uma vírgula (“,”) para um ponto decimal e formatos de data diferentes do inglês dos EUA, você deve tomar medidas para globalizar o aplicativo. Confira o comentário 4076 do GitHub para obter instruções sobre como adicionar casas decimais.

Observe como o formulário renderizou automaticamente uma mensagem de erro de validação apropriada em cada campo que contém um valor inválido. Os erros são impostos no lado do cliente (usando o JavaScript e o jQuery) e no lado do servidor (caso um usuário tenha o JavaScript desabilitado).

Uma vantagem significativa é que você não precisa alterar uma única linha de código na classe MoviesController ou na exibição Create.cshtml para habilitar essa interface do usuário de validação. O controlador e as exibições criados anteriormente neste tutorial selecionaram automaticamente as regras de validação especificadas com atributos de validação nas propriedades da classe de modelo Movie. Teste a validação usando o método de ação Edit e a mesma validação é aplicada.

Os dados de formulário não serão enviados para o servidor enquanto houver erros de validação do lado do cliente. Verifique isso colocando um ponto de interrupção no método HTTP Post usando a ferramenta Fiddler ou as ferramentas do Desenvolvedor F12.

Como funciona a validação

Talvez você esteja se perguntando como a interface do usuário de validação foi gerada sem atualizações do código no controlador ou nas exibições. O código a seguir mostra os dois métodos Create.

// GET: Movies/Create
public IActionResult Create()
{
    return View();
}

// POST: Movies/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (ModelState.IsValid)
    {
        _context.Add(movie);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O primeiro método de ação (HTTP GET) Create exibe o formulário Criar inicial. A segunda versão ([HttpPost]) manipula a postagem de formulário. O segundo método Create (a versão [HttpPost]) chama ModelState.IsValid para verificar se o filme tem erros de validação. A chamada a esse método avalia os atributos de validação que foram aplicados ao objeto. Se o objeto tiver erros de validação, o método Create exibirá o formulário novamente. Se não houver erros, o método salvará o novo filme no banco de dados. Em nosso exemplo de filme, o formulário não é postado no servidor quando há erros de validação detectados no lado do cliente; o segundo método Create nunca é chamado quando há erros de validação do lado do cliente. Se você desabilitar o JavaScript no navegador, a validação do cliente será desabilitada e você poderá testar o método Create HTTP POST ModelState.IsValid detectando erros de validação.

Defina um ponto de interrupção no método [HttpPost] Create e verifique se o método nunca é chamado; a validação do lado do cliente não enviará os dados de formulário quando forem detectados erros de validação. Se você desabilitar o JavaScript no navegador e, em seguida, enviar o formulário com erros, o ponto de interrupção será atingido. Você ainda pode obter uma validação completa sem o JavaScript.

A imagem a seguir mostra como desabilitar o JavaScript no navegador Firefox.

Firefox: Na guia Conteúdo de Opções, desmarque a caixa de seleção Habilitar JavaScript.

A imagem a seguir mostra como desabilitar o JavaScript no navegador Chrome.

Google Chrome: Na seção JavaScript das configurações de Conteúdo, selecione Não permitir que nenhum site execute JavaScript.

Depois de desabilitar o JavaScript, poste os dados inválidos e execute o depurador em etapas.

Durante a depuração em uma postagem de dados inválidos, o IntelliSense em ModelState.IsValid mostra que o valor é falso.

A parte do modo de exibição Create.cshtml é mostrada na seguinte marcação:

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>

            @*Markup removed for brevity.*@

a marcação anterior é usada pelos métodos de ação para exibir o formulário inicial e exibi-lo novamente em caso de erro.

O Auxiliar de Marcação de Entrada usa os atributos de DataAnnotations e produz os atributos HTML necessários para a Validação do jQuery no lado do cliente. O Auxiliar de Marcação de Validação exibe erros de validação. Consulte Validação para obter mais informações.

O que é realmente interessante nessa abordagem é que o controlador nem o modelo de exibição Create sabem nada sobre as regras de validação reais que estão sendo impostas ou as mensagens de erro específicas exibidas. As regras de validação e as cadeias de caracteres de erro são especificadas somente na classe Movie. Essas mesmas regras de validação são aplicadas automaticamente à exibição Edit e a outros modelos de exibição que podem ser criados e que editam o modelo.

Quando você precisar alterar a lógica de validação, faça isso exatamente em um lugar, adicionando atributos de validação ao modelo (neste exemplo, a classe Movie). Você não precisa se preocupar se diferentes partes do aplicativo estão inconsistentes com a forma como as regras são impostas – toda a lógica de validação será definida em um lugar e usada em todos os lugares. Isso mantém o código muito limpo e torna-o mais fácil de manter e desenvolver. Além disso, isso significa que você respeitará totalmente o princípio DRY.

Usando atributos DataType

Abra o arquivo Movie.cs e examine a classe Movie. O namespace System.ComponentModel.DataAnnotations fornece atributos de formatação, além do conjunto interno de atributos de validação. Já aplicamos um valor de enumeração DataType à data de lançamento e aos campos de preço. O código a seguir mostra as propriedades ReleaseDate e Price com o atributo DataType apropriado.

[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }

[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }    

Os atributos DataType fornecem dicas apenas para que o mecanismo de exibição formate os dados e fornece elementos/atributos como <a> para as URLs e <a href="mailto:EmailAddress.com"> para o email. Use o atributo RegularExpression para validar o formato dos dados. O atributo DataType é usado para especificar um tipo de dados mais específico do que o tipo intrínseco de banco de dados; eles não são atributos de validação. Nesse caso, apenas desejamos acompanhar a data, não a hora. A Enumeração DataType fornece muitos tipos de dados, como Date, Time, PhoneNumber, Currency, EmailAddress e muito mais. O atributo DataType também pode permitir que o aplicativo forneça automaticamente recursos específicos a um tipo. Por exemplo, um link mailto: pode ser criado para DataType.EmailAddress e um seletor de data pode ser fornecido para DataType.Date em navegadores que dão suporte a HTML5. Os atributos DataType emitem atributos data- HTML 5 (ou "data dash") que são reconhecidos pelos navegadores HTML 5. Os atributos DataTypenão fornecem nenhuma validação.

DataType.Date não especifica o formato da data exibida. Por padrão, o campo de dados é exibido de acordo com os formatos padrão com base nas CultureInfo do servidor.

O atributo DisplayFormat é usado para especificar explicitamente o formato de data:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

A configuração ApplyFormatInEditMode especifica que a formatação também deve ser aplicada quando o valor é exibido em uma caixa de texto para edição. (Talvez você não deseje isso em alguns campos – por exemplo, para valores de moeda, provavelmente, você não deseja exibir o símbolo de moeda na caixa de texto para edição.)

Você pode usar o atributo DisplayFormat por si só, mas geralmente é uma boa ideia usar o atributo DataType. O atributo DataType transmite a semântica dos dados, ao invés de apresentar como renderizá-lo em uma tela e oferece os seguintes benefícios que você não obtém com DisplayFormat:

  • O navegador pode habilitar os recursos do HTML5 (por exemplo, mostrar um controle de calendário, o símbolo de moeda apropriado à localidade, links de email, etc.)

  • Por padrão, o navegador renderizará os dados usando o formato correto de acordo com a localidade.

  • O atributo DataType pode permitir que o MVC escolha o modelo de campo correto para renderizar os dados (o DisplayFormat, se usado por si só, usa o modelo de cadeia de caracteres).

Observação

A validação do jQuery não funciona com os atributos Range e DateTime. Por exemplo, o seguinte código sempre exibirá um erro de validação do lado do cliente, mesmo quando a data estiver no intervalo especificado:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

Você precisará desabilitar a validação de data do jQuery para usar o atributo Range com DateTime. Geralmente, não é uma boa prática compilar datas rígidas nos modelos e, portanto, o uso do atributo Range e de DateTime não é recomendado.

O seguinte código mostra como combinar atributos em uma linha:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; }
    [Display(Name = "Release Date"), DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$"), Required, StringLength(30)]
    public string Genre { get; set; }
    [Range(1, 100), DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }
    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$"), StringLength(5)]
    public string Rating { get; set; }
}

Na próxima parte da série, examinaremos o aplicativo e faremos algumas melhorias nos métodos Details e Delete gerados automaticamente.

Recursos adicionais

Nesta seção:

  • A lógica de validação é adicionada ao modelo Movie.
  • Você garante que as regras de validação sejam impostas sempre que um usuário criar ou editar um filme.

Mantendo o processo DRY

Um dos princípios de design do MVC é o DRY (“Don't Repeat Yourself”). O ASP.NET Core MVC incentiva você a especificar a funcionalidade ou o comportamento somente uma vez e, em seguida, refleti-lo em qualquer lugar de um aplicativo. Isso reduz a quantidade de código que você precisa escrever e faz com que o código escrito seja menos propenso a erros e mais fácil de testar e manter.

O suporte de validação fornecido pelo MVC e pelo Entity Framework Core Code First é um bom exemplo do princípio DRY em ação. Especifique as regras de validação de forma declarativa em um único lugar (na classe de modelo) e as regras são impostas em qualquer lugar no aplicativo.

Adicionar regras de validação ao modelo de filme

O namespace DataAnnotations fornece um conjunto de atributos de validação internos aplicados de forma declarativa a uma classe ou propriedade. DataAnnotations também contém atributos de formatação como DataType, que ajudam com a formatação e não fornecem validação.

Atualize a classe Movie para aproveitar os atributos de validação internos Required, StringLength, RegularExpression e Range o atributo de formatação DataType.

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }

    [StringLength(60, MinimumLength = 3)]
    [Required]
    public string? Title { get; set; }

    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }    

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
    [Required]
    [StringLength(30)]
    public string? Genre { get; set; }
    
    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
    [StringLength(5)]
    [Required]
    public string? Rating { get; set; }
}

Os atributos de validação especificam o comportamento que você deseja impor nas propriedades de modelo às quais eles são aplicados:

  • Os atributos Required e MinimumLength indicam que uma propriedade deve ter um valor; porém, nada impede que um usuário insira um espaço em branco para atender a essa validação.

  • O atributo RegularExpression é usado para limitar quais caracteres podem ser inseridos. No código anterior, "Gênero":

    • Deve usar apenas letras.
    • A primeira letra deve ser maiúscula. Espaços em branco são permitidos, enquanto números e caracteres especiais não são.
  • A "Classificação" RegularExpression:

    • Exige que o primeiro caractere seja uma letra maiúscula.
    • Permite caracteres especiais e números nos espaços subsequentes. "PG-13" é válido para uma classificação, mas é um erro em "Gênero".
  • O atributo Range restringe um valor a um intervalo especificado.

  • O atributo StringLength permite definir o tamanho máximo de uma propriedade de cadeia de caracteres e, opcionalmente, seu tamanho mínimo.

  • Os tipos de valor (como decimal, int, float, DateTime) são inerentemente necessários e não precisam do atributo [Required].

Ter as regras de validação automaticamente impostas pelo ASP.NET Core ajuda a tornar seu aplicativo mais robusto. Também garante que você não se esqueça de validar algo e inadvertidamente permita dados incorretos no banco de dados.

Interface do usuário de erro de validação

Execute o aplicativo e navegue para o controlador Movies.

Selecione o link Criar Novo para adicionar um novo filme. Preencha o formulário com alguns valores inválidos. Assim que a validação do lado do cliente do jQuery detecta o erro, ela exibe uma mensagem de erro.

Formulário da exibição de filmes com vários erros de validação do lado do cliente do jQuery

Observação

Talvez você não consiga inserir vírgulas decimais em campos decimais. Para dar suporte à validação do jQuery para localidades de idiomas diferentes do inglês que usam uma vírgula (“,”) para um ponto decimal e formatos de data diferentes do inglês dos EUA, você deve tomar medidas para globalizar o aplicativo. Confira o comentário 4076 do GitHub para obter instruções sobre como adicionar casas decimais.

Observe como o formulário renderizou automaticamente uma mensagem de erro de validação apropriada em cada campo que contém um valor inválido. Os erros são impostos no lado do cliente (usando o JavaScript e o jQuery) e no lado do servidor (caso um usuário tenha o JavaScript desabilitado).

Uma vantagem significativa é que você não precisa alterar uma única linha de código na classe MoviesController ou na exibição Create.cshtml para habilitar essa interface do usuário de validação. O controlador e as exibições criados anteriormente neste tutorial selecionaram automaticamente as regras de validação especificadas com atributos de validação nas propriedades da classe de modelo Movie. Teste a validação usando o método de ação Edit e a mesma validação é aplicada.

Os dados de formulário não serão enviados para o servidor enquanto houver erros de validação do lado do cliente. Verifique isso colocando um ponto de interrupção no método HTTP Post usando a ferramenta Fiddler ou as ferramentas do Desenvolvedor F12.

Como funciona a validação

Talvez você esteja se perguntando como a interface do usuário de validação foi gerada sem atualizações do código no controlador ou nas exibições. O código a seguir mostra os dois métodos Create.

// GET: Movies/Create
public IActionResult Create()
{
    return View();
}

// POST: Movies/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (ModelState.IsValid)
    {
        _context.Add(movie);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O primeiro método de ação (HTTP GET) Create exibe o formulário Criar inicial. A segunda versão ([HttpPost]) manipula a postagem de formulário. O segundo método Create (a versão [HttpPost]) chama ModelState.IsValid para verificar se o filme tem erros de validação. A chamada a esse método avalia os atributos de validação que foram aplicados ao objeto. Se o objeto tiver erros de validação, o método Create exibirá o formulário novamente. Se não houver erros, o método salvará o novo filme no banco de dados. Em nosso exemplo de filme, o formulário não é postado no servidor quando há erros de validação detectados no lado do cliente; o segundo método Create nunca é chamado quando há erros de validação do lado do cliente. Se você desabilitar o JavaScript no navegador, a validação do cliente será desabilitada e você poderá testar o método Create HTTP POST ModelState.IsValid detectando erros de validação.

Defina um ponto de interrupção no método [HttpPost] Create e verifique se o método nunca é chamado; a validação do lado do cliente não enviará os dados de formulário quando forem detectados erros de validação. Se você desabilitar o JavaScript no navegador e, em seguida, enviar o formulário com erros, o ponto de interrupção será atingido. Você ainda pode obter uma validação completa sem o JavaScript.

A imagem a seguir mostra como desabilitar o JavaScript no navegador Firefox.

Firefox: Na guia Conteúdo de Opções, desmarque a caixa de seleção Habilitar JavaScript.

A imagem a seguir mostra como desabilitar o JavaScript no navegador Chrome.

Google Chrome: Na seção JavaScript das configurações de Conteúdo, selecione Não permitir que nenhum site execute JavaScript.

Depois de desabilitar o JavaScript, poste os dados inválidos e execute o depurador em etapas.

Durante a depuração em uma postagem de dados inválidos, o IntelliSense em ModelState.IsValid mostra que o valor é falso.

A parte do modo de exibição Create.cshtml é mostrada na seguinte marcação:

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>

            @*Markup removed for brevity.*@

a marcação anterior é usada pelos métodos de ação para exibir o formulário inicial e exibi-lo novamente em caso de erro.

O Auxiliar de Marcação de Entrada usa os atributos de DataAnnotations e produz os atributos HTML necessários para a Validação do jQuery no lado do cliente. O Auxiliar de Marcação de Validação exibe erros de validação. Consulte Validação para obter mais informações.

O que é realmente interessante nessa abordagem é que o controlador nem o modelo de exibição Create sabem nada sobre as regras de validação reais que estão sendo impostas ou as mensagens de erro específicas exibidas. As regras de validação e as cadeias de caracteres de erro são especificadas somente na classe Movie. Essas mesmas regras de validação são aplicadas automaticamente à exibição Edit e a outros modelos de exibição que podem ser criados e que editam o modelo.

Quando você precisar alterar a lógica de validação, faça isso exatamente em um lugar, adicionando atributos de validação ao modelo (neste exemplo, a classe Movie). Você não precisa se preocupar se diferentes partes do aplicativo estão inconsistentes com a forma como as regras são impostas – toda a lógica de validação será definida em um lugar e usada em todos os lugares. Isso mantém o código muito limpo e torna-o mais fácil de manter e desenvolver. Além disso, isso significa que você respeitará totalmente o princípio DRY.

Usando atributos DataType

Abra o arquivo Movie.cs e examine a classe Movie. O namespace System.ComponentModel.DataAnnotations fornece atributos de formatação, além do conjunto interno de atributos de validação. Já aplicamos um valor de enumeração DataType à data de lançamento e aos campos de preço. O código a seguir mostra as propriedades ReleaseDate e Price com o atributo DataType apropriado.

[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }

[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }    

Os atributos DataType fornecem dicas apenas para que o mecanismo de exibição formate os dados e fornece elementos/atributos como <a> para as URLs e <a href="mailto:EmailAddress.com"> para o email. Use o atributo RegularExpression para validar o formato dos dados. O atributo DataType é usado para especificar um tipo de dados mais específico do que o tipo intrínseco de banco de dados; eles não são atributos de validação. Nesse caso, apenas desejamos acompanhar a data, não a hora. A Enumeração DataType fornece muitos tipos de dados, como Date, Time, PhoneNumber, Currency, EmailAddress e muito mais. O atributo DataType também pode permitir que o aplicativo forneça automaticamente recursos específicos a um tipo. Por exemplo, um link mailto: pode ser criado para DataType.EmailAddress e um seletor de data pode ser fornecido para DataType.Date em navegadores que dão suporte a HTML5. Os atributos DataType emitem atributos data- HTML 5 (ou "data dash") que são reconhecidos pelos navegadores HTML 5. Os atributos DataTypenão fornecem nenhuma validação.

DataType.Date não especifica o formato da data exibida. Por padrão, o campo de dados é exibido de acordo com os formatos padrão com base nas CultureInfo do servidor.

O atributo DisplayFormat é usado para especificar explicitamente o formato de data:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

A configuração ApplyFormatInEditMode especifica que a formatação também deve ser aplicada quando o valor é exibido em uma caixa de texto para edição. (Talvez você não deseje isso em alguns campos – por exemplo, para valores de moeda, provavelmente, você não deseja exibir o símbolo de moeda na caixa de texto para edição.)

Você pode usar o atributo DisplayFormat por si só, mas geralmente é uma boa ideia usar o atributo DataType. O atributo DataType transmite a semântica dos dados, ao invés de apresentar como renderizá-lo em uma tela e oferece os seguintes benefícios que você não obtém com DisplayFormat:

  • O navegador pode habilitar os recursos do HTML5 (por exemplo, mostrar um controle de calendário, o símbolo de moeda apropriado à localidade, links de email, etc.)

  • Por padrão, o navegador renderizará os dados usando o formato correto de acordo com a localidade.

  • O atributo DataType pode permitir que o MVC escolha o modelo de campo correto para renderizar os dados (o DisplayFormat, se usado por si só, usa o modelo de cadeia de caracteres).

Observação

A validação do jQuery não funciona com os atributos Range e DateTime. Por exemplo, o seguinte código sempre exibirá um erro de validação do lado do cliente, mesmo quando a data estiver no intervalo especificado:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

Você precisará desabilitar a validação de data do jQuery para usar o atributo Range com DateTime. Geralmente, não é uma boa prática compilar datas rígidas nos modelos e, portanto, o uso do atributo Range e de DateTime não é recomendado.

O seguinte código mostra como combinar atributos em uma linha:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; }
    [Display(Name = "Release Date"), DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$"), Required, StringLength(30)]
    public string Genre { get; set; }
    [Range(1, 100), DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }
    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$"), StringLength(5)]
    public string Rating { get; set; }
}

Na próxima parte da série, examinaremos o aplicativo e faremos algumas melhorias nos métodos Details e Delete gerados automaticamente.

Recursos adicionais

Nesta seção:

  • A lógica de validação é adicionada ao modelo Movie.
  • Você garante que as regras de validação sejam impostas sempre que um usuário criar ou editar um filme.

Mantendo o processo DRY

Um dos princípios de design do MVC é o DRY (“Don't Repeat Yourself”). O ASP.NET Core MVC incentiva você a especificar a funcionalidade ou o comportamento somente uma vez e, em seguida, refleti-lo em qualquer lugar de um aplicativo. Isso reduz a quantidade de código que você precisa escrever e faz com que o código escrito seja menos propenso a erros e mais fácil de testar e manter.

O suporte de validação fornecido pelo MVC e pelo Entity Framework Core Code First é um bom exemplo do princípio DRY em ação. Especifique as regras de validação de forma declarativa em um único lugar (na classe de modelo) e as regras são impostas em qualquer lugar no aplicativo.

Adicionar regras de validação ao modelo de filme

O namespace DataAnnotations fornece um conjunto de atributos de validação internos aplicados de forma declarativa a uma classe ou propriedade. DataAnnotations também contém atributos de formatação como DataType, que ajudam com a formatação e não fornecem validação.

Atualize a classe Movie para aproveitar os atributos de validação Required, StringLength, RegularExpression e Range internos.

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }

        [StringLength(60, MinimumLength = 3)]
        [Required]
        public string? Title { get; set; }

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }

        [Range(1, 100)]
        [DataType(DataType.Currency)]
        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
        [Required]
        [StringLength(30)]
        public string? Genre { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
        [StringLength(5)]
        [Required]
        public string? Rating { get; set; }
    }
}

Os atributos de validação especificam o comportamento que você deseja impor nas propriedades de modelo às quais eles são aplicados:

  • Os atributos Required e MinimumLength indicam que uma propriedade deve ter um valor; porém, nada impede que um usuário insira um espaço em branco para atender a essa validação.

  • O atributo RegularExpression é usado para limitar quais caracteres podem ser inseridos. No código anterior, "Gênero":

    • Deve usar apenas letras.
    • A primeira letra deve ser maiúscula. Espaços em branco são permitidos, enquanto números e caracteres especiais não são.
  • A "Classificação" RegularExpression:

    • Exige que o primeiro caractere seja uma letra maiúscula.
    • Permite caracteres especiais e números nos espaços subsequentes. "PG-13" é válido para uma classificação, mas é um erro em "Gênero".
  • O atributo Range restringe um valor a um intervalo especificado.

  • O atributo StringLength permite definir o tamanho máximo de uma propriedade de cadeia de caracteres e, opcionalmente, seu tamanho mínimo.

  • Os tipos de valor (como decimal, int, float, DateTime) são inerentemente necessários e não precisam do atributo [Required].

Ter as regras de validação automaticamente impostas pelo ASP.NET Core ajuda a tornar seu aplicativo mais robusto. Também garante que você não se esqueça de validar algo e inadvertidamente permita dados incorretos no banco de dados.

Interface do usuário de erro de validação

Execute o aplicativo e navegue para o controlador Movies.

Selecione o link Criar Novo para adicionar um novo filme. Preencha o formulário com alguns valores inválidos. Assim que a validação do lado do cliente do jQuery detecta o erro, ela exibe uma mensagem de erro.

Formulário da exibição de filmes com vários erros de validação do lado do cliente do jQuery

Observação

Talvez você não consiga inserir vírgulas decimais em campos decimais. Para dar suporte à validação do jQuery para localidades de idiomas diferentes do inglês que usam uma vírgula (“,”) para um ponto decimal e formatos de data diferentes do inglês dos EUA, você deve tomar medidas para globalizar o aplicativo. Confira o comentário 4076 do GitHub para obter instruções sobre como adicionar casas decimais.

Observe como o formulário renderizou automaticamente uma mensagem de erro de validação apropriada em cada campo que contém um valor inválido. Os erros são impostos no lado do cliente (usando o JavaScript e o jQuery) e no lado do servidor (caso um usuário tenha o JavaScript desabilitado).

Uma vantagem significativa é que você não precisa alterar uma única linha de código na classe MoviesController ou na exibição Create.cshtml para habilitar essa interface do usuário de validação. O controlador e as exibições criados anteriormente neste tutorial selecionaram automaticamente as regras de validação especificadas com atributos de validação nas propriedades da classe de modelo Movie. Teste a validação usando o método de ação Edit e a mesma validação é aplicada.

Os dados de formulário não serão enviados para o servidor enquanto houver erros de validação do lado do cliente. Verifique isso colocando um ponto de interrupção no método HTTP Post usando a ferramenta Fiddler ou as ferramentas do Desenvolvedor F12.

Como funciona a validação

Talvez você esteja se perguntando como a interface do usuário de validação foi gerada sem atualizações do código no controlador ou nas exibições. O código a seguir mostra os dois métodos Create.

// GET: Movies/Create
public IActionResult Create()
{
    return View();
}

// POST: Movies/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (ModelState.IsValid)
    {
        _context.Add(movie);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O primeiro método de ação (HTTP GET) Create exibe o formulário Criar inicial. A segunda versão ([HttpPost]) manipula a postagem de formulário. O segundo método Create (a versão [HttpPost]) chama ModelState.IsValid para verificar se o filme tem erros de validação. A chamada a esse método avalia os atributos de validação que foram aplicados ao objeto. Se o objeto tiver erros de validação, o método Create exibirá o formulário novamente. Se não houver erros, o método salvará o novo filme no banco de dados. Em nosso exemplo de filme, o formulário não é postado no servidor quando há erros de validação detectados no lado do cliente; o segundo método Create nunca é chamado quando há erros de validação do lado do cliente. Se você desabilitar o JavaScript no navegador, a validação do cliente será desabilitada e você poderá testar o método Create HTTP POST ModelState.IsValid detectando erros de validação.

Defina um ponto de interrupção no método [HttpPost] Create e verifique se o método nunca é chamado; a validação do lado do cliente não enviará os dados de formulário quando forem detectados erros de validação. Se você desabilitar o JavaScript no navegador e, em seguida, enviar o formulário com erros, o ponto de interrupção será atingido. Você ainda pode obter uma validação completa sem o JavaScript.

A imagem a seguir mostra como desabilitar o JavaScript no navegador Firefox.

Firefox: Na guia Conteúdo de Opções, desmarque a caixa de seleção Habilitar JavaScript.

A imagem a seguir mostra como desabilitar o JavaScript no navegador Chrome.

Google Chrome: Na seção JavaScript das configurações de Conteúdo, selecione Não permitir que nenhum site execute JavaScript.

Depois de desabilitar o JavaScript, poste os dados inválidos e execute o depurador em etapas.

Durante a depuração em uma postagem de dados inválidos, o IntelliSense em ModelState.IsValid mostra que o valor é falso.

A parte do modo de exibição Create.cshtml é mostrada na seguinte marcação:

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>

            @*Markup removed for brevity.*@

a marcação anterior é usada pelos métodos de ação para exibir o formulário inicial e exibi-lo novamente em caso de erro.

O Auxiliar de Marcação de Entrada usa os atributos de DataAnnotations e produz os atributos HTML necessários para a Validação do jQuery no lado do cliente. O Auxiliar de Marcação de Validação exibe erros de validação. Consulte Validação para obter mais informações.

O que é realmente interessante nessa abordagem é que o controlador nem o modelo de exibição Create sabem nada sobre as regras de validação reais que estão sendo impostas ou as mensagens de erro específicas exibidas. As regras de validação e as cadeias de caracteres de erro são especificadas somente na classe Movie. Essas mesmas regras de validação são aplicadas automaticamente à exibição Edit e a outros modelos de exibição que podem ser criados e que editam o modelo.

Quando você precisar alterar a lógica de validação, faça isso exatamente em um lugar, adicionando atributos de validação ao modelo (neste exemplo, a classe Movie). Você não precisa se preocupar se diferentes partes do aplicativo estão inconsistentes com a forma como as regras são impostas – toda a lógica de validação será definida em um lugar e usada em todos os lugares. Isso mantém o código muito limpo e torna-o mais fácil de manter e desenvolver. Além disso, isso significa que você respeitará totalmente o princípio DRY.

Usando atributos DataType

Abra o arquivo Movie.cs e examine a classe Movie. O namespace System.ComponentModel.DataAnnotations fornece atributos de formatação, além do conjunto interno de atributos de validação. Já aplicamos um valor de enumeração DataType à data de lançamento e aos campos de preço. O código a seguir mostra as propriedades ReleaseDate e Price com o atributo DataType apropriado.

[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }

[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }

Os atributos DataType fornecem dicas apenas para que o mecanismo de exibição formate os dados e fornece elementos/atributos como <a> para as URLs e <a href="mailto:EmailAddress.com"> para o email. Use o atributo RegularExpression para validar o formato dos dados. O atributo DataType é usado para especificar um tipo de dados mais específico do que o tipo intrínseco de banco de dados; eles não são atributos de validação. Nesse caso, apenas desejamos acompanhar a data, não a hora. A Enumeração DataType fornece muitos tipos de dados, como Date, Time, PhoneNumber, Currency, EmailAddress e muito mais. O atributo DataType também pode permitir que o aplicativo forneça automaticamente recursos específicos a um tipo. Por exemplo, um link mailto: pode ser criado para DataType.EmailAddress e um seletor de data pode ser fornecido para DataType.Date em navegadores que dão suporte a HTML5. Os atributos DataType emitem atributos data- HTML 5 (ou "data dash") que são reconhecidos pelos navegadores HTML 5. Os atributos DataTypenão fornecem nenhuma validação.

DataType.Date não especifica o formato da data exibida. Por padrão, o campo de dados é exibido de acordo com os formatos padrão com base nas CultureInfo do servidor.

O atributo DisplayFormat é usado para especificar explicitamente o formato de data:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

A configuração ApplyFormatInEditMode especifica que a formatação também deve ser aplicada quando o valor é exibido em uma caixa de texto para edição. (Talvez você não deseje isso em alguns campos – por exemplo, para valores de moeda, provavelmente, você não deseja exibir o símbolo de moeda na caixa de texto para edição.)

Você pode usar o atributo DisplayFormat por si só, mas geralmente é uma boa ideia usar o atributo DataType. O atributo DataType transmite a semântica dos dados, ao invés de apresentar como renderizá-lo em uma tela e oferece os seguintes benefícios que você não obtém com DisplayFormat:

  • O navegador pode habilitar os recursos do HTML5 (por exemplo, mostrar um controle de calendário, o símbolo de moeda apropriado à localidade, links de email, etc.)

  • Por padrão, o navegador renderizará os dados usando o formato correto de acordo com a localidade.

  • O atributo DataType pode permitir que o MVC escolha o modelo de campo correto para renderizar os dados (o DisplayFormat, se usado por si só, usa o modelo de cadeia de caracteres).

Observação

A validação do jQuery não funciona com os atributos Range e DateTime. Por exemplo, o seguinte código sempre exibirá um erro de validação do lado do cliente, mesmo quando a data estiver no intervalo especificado:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

Você precisará desabilitar a validação de data do jQuery para usar o atributo Range com DateTime. Geralmente, não é uma boa prática compilar datas rígidas nos modelos e, portanto, o uso do atributo Range e de DateTime não é recomendado.

O seguinte código mostra como combinar atributos em uma linha:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }

        [StringLength(60, MinimumLength = 3)]
        public string Title { get; set; }

        [Display(Name = "Release Date"), DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$"), Required, StringLength(30)]
        public string Genre { get; set; }

        [Range(1, 100), DataType(DataType.Currency)]
        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$"), StringLength(5)]
        public string Rating { get; set; }
    }
}

Na próxima parte da série, examinaremos o aplicativo e faremos algumas melhorias nos métodos Details e Delete gerados automaticamente.

Recursos adicionais

Nesta seção:

  • A lógica de validação é adicionada ao modelo Movie.
  • Você garante que as regras de validação sejam impostas sempre que um usuário criar ou editar um filme.

Mantendo o processo DRY

Um dos princípios de design do MVC é o DRY (“Don't Repeat Yourself”). O ASP.NET Core MVC incentiva você a especificar a funcionalidade ou o comportamento somente uma vez e, em seguida, refleti-lo em qualquer lugar de um aplicativo. Isso reduz a quantidade de código que você precisa escrever e faz com que o código escrito seja menos propenso a erros e mais fácil de testar e manter.

O suporte de validação fornecido pelo MVC e pelo Entity Framework Core Code First é um bom exemplo do princípio DRY em ação. Especifique as regras de validação de forma declarativa em um único lugar (na classe de modelo) e as regras são impostas em qualquer lugar no aplicativo.

Adicionar regras de validação ao modelo de filme

O namespace DataAnnotations fornece um conjunto de atributos de validação internos aplicados de forma declarativa a uma classe ou propriedade. DataAnnotations também contém atributos de formatação como DataType, que ajudam com a formatação e não fornecem validação.

Atualize a classe Movie para aproveitar os atributos de validação Required, StringLength, RegularExpression e Range internos.

public class Movie
{
    public int Id { get; set; }

    [StringLength(60, MinimumLength = 3)]
    [Required]
    public string Title { get; set; }

    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
    [Required]
    [StringLength(30)]
    public string Genre { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
    [StringLength(5)]
    [Required]
    public string Rating { get; set; }
}

Os atributos de validação especificam o comportamento que você deseja impor nas propriedades de modelo às quais eles são aplicados:

  • Os atributos Required e MinimumLength indicam que uma propriedade deve ter um valor; porém, nada impede que um usuário insira um espaço em branco para atender a essa validação.

  • O atributo RegularExpression é usado para limitar quais caracteres podem ser inseridos. No código anterior, "Gênero":

    • Deve usar apenas letras.
    • A primeira letra precisa ser maiúscula. Espaços em branco são permitidos, enquanto números e caracteres especiais não são.
  • A "Classificação" RegularExpression:

    • Exige que o primeiro caractere seja uma letra maiúscula.
    • Permite caracteres especiais e números nos espaços subsequentes. "PG-13" é válido para uma classificação, mas é um erro em "Gênero".
  • O atributo Range restringe um valor a um intervalo especificado.

  • O atributo StringLength permite definir o tamanho máximo de uma propriedade de cadeia de caracteres e, opcionalmente, seu tamanho mínimo.

  • Os tipos de valor (como decimal, int, float, DateTime) são inerentemente necessários e não precisam do atributo [Required].

Ter as regras de validação automaticamente impostas pelo ASP.NET Core ajuda a tornar seu aplicativo mais robusto. Também garante que você não se esqueça de validar algo e inadvertidamente permita dados incorretos no banco de dados.

Interface do usuário de erro de validação

Execute o aplicativo e navegue para o controlador Movies.

Toque no link Criar Novo para adicionar um novo filme. Preencha o formulário com alguns valores inválidos. Assim que a validação do lado do cliente do jQuery detecta o erro, ela exibe uma mensagem de erro.

Formulário da exibição de filmes com vários erros de validação do lado do cliente do jQuery

Observação

Talvez você não consiga inserir vírgulas decimais em campos decimais. Para dar suporte à validação do jQuery para localidades de idiomas diferentes do inglês que usam uma vírgula (“,”) para um ponto decimal e formatos de data diferentes do inglês dos EUA, você deve tomar medidas para globalizar o aplicativo. Confira o comentário 4076 do GitHub para obter instruções sobre como adicionar casas decimais.

Observe como o formulário renderizou automaticamente uma mensagem de erro de validação apropriada em cada campo que contém um valor inválido. Os erros são impostos no lado do cliente (usando o JavaScript e o jQuery) e no lado do servidor (caso um usuário tenha o JavaScript desabilitado).

Uma vantagem significativa é que você não precisa alterar uma única linha de código na classe MoviesController ou na exibição Create.cshtml para habilitar essa interface do usuário de validação. O controlador e as exibições criados anteriormente neste tutorial selecionaram automaticamente as regras de validação especificadas com atributos de validação nas propriedades da classe de modelo Movie. Teste a validação usando o método de ação Edit e a mesma validação é aplicada.

Os dados de formulário não serão enviados para o servidor enquanto houver erros de validação do lado do cliente. Verifique isso colocando um ponto de interrupção no método HTTP Post usando a ferramenta Fiddler ou as ferramentas do Desenvolvedor F12.

Como funciona a validação

Talvez você esteja se perguntando como a interface do usuário de validação foi gerada sem atualizações do código no controlador ou nas exibições. O código a seguir mostra os dois métodos Create.

// GET: Movies/Create
public IActionResult Create()
{
    return View();
}

// POST: Movies/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
    [Bind("ID,Title,ReleaseDate,Genre,Price, Rating")] Movie movie)
{
    if (ModelState.IsValid)
    {
        _context.Add(movie);
        await _context.SaveChangesAsync();
        return RedirectToAction("Index");
    }
    return View(movie);
}

O primeiro método de ação (HTTP GET) Create exibe o formulário Criar inicial. A segunda versão ([HttpPost]) manipula a postagem de formulário. O segundo método Create (a versão [HttpPost]) chama ModelState.IsValid para verificar se o filme tem erros de validação. A chamada a esse método avalia os atributos de validação que foram aplicados ao objeto. Se o objeto tiver erros de validação, o método Create exibirá o formulário novamente. Se não houver erros, o método salvará o novo filme no banco de dados. Em nosso exemplo de filme, o formulário não é postado no servidor quando há erros de validação detectados no lado do cliente; o segundo método Create nunca é chamado quando há erros de validação do lado do cliente. Se você desabilitar o JavaScript no navegador, a validação do cliente será desabilitada e você poderá testar o método Create HTTP POST ModelState.IsValid detectando erros de validação.

Defina um ponto de interrupção no método [HttpPost] Create e verifique se o método nunca é chamado; a validação do lado do cliente não enviará os dados de formulário quando forem detectados erros de validação. Se você desabilitar o JavaScript no navegador e, em seguida, enviar o formulário com erros, o ponto de interrupção será atingido. Você ainda pode obter uma validação completa sem o JavaScript.

A imagem a seguir mostra como desabilitar o JavaScript no navegador Firefox.

Firefox: Na guia Conteúdo de Opções, desmarque a caixa de seleção Habilitar JavaScript.

A imagem a seguir mostra como desabilitar o JavaScript no navegador Chrome.

Google Chrome: Na seção JavaScript das configurações de Conteúdo, selecione Não permitir que nenhum site execute JavaScript.

Depois de desabilitar o JavaScript, poste os dados inválidos e execute o depurador em etapas.

Durante a depuração em uma postagem de dados inválidos, o IntelliSense em ModelState.IsValid mostra que o valor é falso.

A parte do modelo de exibição Create.cshtml é mostrada na seguinte marcação:


<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>           
       
        @*Markup removed for brevity.*@

a marcação anterior é usada pelos métodos de ação para exibir o formulário inicial e exibi-lo novamente em caso de erro.

O Auxiliar de Marcação de Entrada usa os atributos de DataAnnotations e produz os atributos HTML necessários para a Validação do jQuery no lado do cliente. O Auxiliar de Marcação de Validação exibe erros de validação. Consulte Validação para obter mais informações.

O que é realmente interessante nessa abordagem é que o controlador nem o modelo de exibição Create sabem nada sobre as regras de validação reais que estão sendo impostas ou as mensagens de erro específicas exibidas. As regras de validação e as cadeias de caracteres de erro são especificadas somente na classe Movie. Essas mesmas regras de validação são aplicadas automaticamente à exibição Edit e a outros modelos de exibição que podem ser criados e que editam o modelo.

Quando você precisar alterar a lógica de validação, faça isso exatamente em um lugar, adicionando atributos de validação ao modelo (neste exemplo, a classe Movie). Você não precisa se preocupar se diferentes partes do aplicativo estão inconsistentes com a forma como as regras são impostas – toda a lógica de validação será definida em um lugar e usada em todos os lugares. Isso mantém o código muito limpo e torna-o mais fácil de manter e desenvolver. Além disso, isso significa que você respeitará totalmente o princípio DRY.

Usando atributos DataType

Abra o arquivo Movie.cs e examine a classe Movie. O namespace System.ComponentModel.DataAnnotations fornece atributos de formatação, além do conjunto interno de atributos de validação. Já aplicamos um valor de enumeração DataType à data de lançamento e aos campos de preço. O código a seguir mostra as propriedades ReleaseDate e Price com o atributo DataType apropriado.

[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }

[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }

Os atributos DataType fornecem dicas apenas para que o mecanismo de exibição formate os dados (e fornece atributos como <a> para as URLs e <a href="mailto:EmailAddress.com"> para o email). Use o atributo RegularExpression para validar o formato dos dados. O atributo DataType é usado para especificar um tipo de dados mais específico do que o tipo intrínseco de banco de dados; eles não são atributos de validação. Nesse caso, apenas desejamos acompanhar a data, não a hora. A Enumeração DataType fornece muitos tipos de dados, como Date, Time, PhoneNumber, Currency, EmailAddress e muito mais. O atributo DataType também pode permitir que o aplicativo forneça automaticamente recursos específicos a um tipo. Por exemplo, um link mailto: pode ser criado para DataType.EmailAddress e um seletor de data pode ser fornecido para DataType.Date em navegadores que dão suporte a HTML5. Os atributos DataType emitem atributos data- HTML 5 (ou "data dash") que são reconhecidos pelos navegadores HTML 5. Os atributos DataTypenão fornecem nenhuma validação.

DataType.Date não especifica o formato da data exibida. Por padrão, o campo de dados é exibido de acordo com os formatos padrão com base nas CultureInfo do servidor.

O atributo DisplayFormat é usado para especificar explicitamente o formato de data:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

A configuração ApplyFormatInEditMode especifica que a formatação também deve ser aplicada quando o valor é exibido em uma caixa de texto para edição. (Talvez você não deseje isso em alguns campos – por exemplo, para valores de moeda, provavelmente, você não deseja exibir o símbolo de moeda na caixa de texto para edição.)

Você pode usar o atributo DisplayFormat por si só, mas geralmente é uma boa ideia usar o atributo DataType. O atributo DataType transmite a semântica dos dados, ao invés de apresentar como renderizá-lo em uma tela e oferece os seguintes benefícios que você não obtém com DisplayFormat:

  • O navegador pode habilitar os recursos do HTML5 (por exemplo, mostrar um controle de calendário, o símbolo de moeda apropriado à localidade, links de email, etc.)

  • Por padrão, o navegador renderizará os dados usando o formato correto de acordo com a localidade.

  • O atributo DataType pode permitir que o MVC escolha o modelo de campo correto para renderizar os dados (o DisplayFormat, se usado por si só, usa o modelo de cadeia de caracteres).

Observação

A validação do jQuery não funciona com os atributos Range e DateTime. Por exemplo, o seguinte código sempre exibirá um erro de validação do lado do cliente, mesmo quando a data estiver no intervalo especificado:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

Você precisará desabilitar a validação de data do jQuery para usar o atributo Range com DateTime. Geralmente, não é uma boa prática compilar datas rígidas nos modelos e, portanto, o uso do atributo Range e de DateTime não é recomendado.

O seguinte código mostra como combinar atributos em uma linha:

public class Movie
{
    public int Id { get; set; }

    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; }

    [Display(Name = "Release Date"), DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$"), Required, StringLength(30)]
    public string Genre { get; set; }

    [Range(1, 100), DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$"), StringLength(5)]
    public string Rating { get; set; }
}

Na próxima parte da série, examinaremos o aplicativo e faremos algumas melhorias nos métodos Details e Delete gerados automaticamente.

Recursos adicionais