Compartilhar via


Adicionar validação

por Rick Anderson

Observação

Uma versão atualizada deste tutorial está disponível aqui usando a versão mais recente do Visual Studio. O novo tutorial usa ASP.NET Core MVC, que fornece muitas melhorias em relação a este tutorial.

Este tutorial ensina a usar o ASP.NET Core MVC com controladores e exibições. O Razor Pages é uma nova alternativa no ASP.NET Core, um modelo de programação baseado em página que torna a criação da interface do usuário da Web mais fácil e produtiva. É recomendável que você tente o tutorial das Páginas Razor antes da versão do MVC. O tutorial Páginas do Razor:

  • É mais fácil de acompanhar.
  • Aborda mais recursos.
  • É a abordagem preferencial para o desenvolvimento de novos aplicativos.

Nesta seção, você adicionará lógica de validação ao Movie modelo e garantirá que as regras de validação sejam impostas sempre que um usuário tentar criar ou editar um filme usando o aplicativo.

Mantendo as coisas SECAS

Um dos princípios básicos de design do ASP.NET MVC é DRY ("Don't Repeat Yourself"). ASP.NET MVC incentiva você a especificar a funcionalidade ou o comportamento apenas uma vez e, em seguida, fazer com que ele seja refletido em todos os lugares em um aplicativo. Isso reduz a quantidade de código que você precisa escrever e torna o código que você escreve menos propenso a erros e mais fácil de manter.

O suporte de validação fornecido pelo MVC e ASP.NET pelo Entity Framework Code First é um ótimo exemplo do princípio DRY em ação. Você pode especificar declarativamente regras de validação em um só lugar (na classe de modelo) e as regras são impostas em todos os lugares do aplicativo.

Vejamos como você pode aproveitar esse suporte de validação no aplicativo de filme.

Adicionando regras de validação ao modelo de filme

Você começará adicionando alguma lógica de validação à Movie classe.

Abra o arquivo Movie.cs. Observe que o System.ComponentModel.DataAnnotations namespace não contém System.Web. DataAnnotations fornece um conjunto interno de atributos de validação que você pode aplicar declarativamente a qualquer classe ou propriedade. (Ele também contém atributos de formatação como DataType que ajudam na formatação e não fornecem nenhuma validação.)

Agora, atualize a Movie classe para aproveitar os atributos internos Required, StringLength, RegularExpression e Range validation. Substitua a Movie classe pelo seguinte:

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

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

    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    public DateTime ReleaseDate { get; set; }
  
    [RegularExpression(@"^[A-Z]+[a-zA-Z]*$")]
    [Required]
    [StringLength(30)]
    public string Genre { get; set; }

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

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

O StringLength atributo define o comprimento máximo da cadeia de caracteres e define essa limitação no banco de dados, portanto, o esquema do banco de dados será alterado. Clique com o botão direito do mouse na tabela Filmes no Gerenciador de servidores e clique em Abrir definição de tabela:

Captura de tela que mostra a janela do Gerenciador de Servidores aberta e na guia Design de filmes.

Na imagem acima, você pode ver que todos os campos de cadeia de caracteres estão definidos como NVARCHAR (MAX). Usaremos migrações para atualizar o esquema. Compile a solução e, em seguida, abra a janela Console do Gerenciador de Pacotes e insira os seguintes comandos:

add-migration DataAnnotations
update-database

Quando esse comando for concluído, o Visual Studio abrirá o arquivo de classe que define a nova DbMigration classe derivada com o nome especificado (DataAnnotations) e, no Up método, você poderá ver o código que atualiza as restrições de esquema:

public override void Up()
{
    AlterColumn("dbo.Movies", "Title", c => c.String(maxLength: 60));
    AlterColumn("dbo.Movies", "Genre", c => c.String(nullable: false, maxLength: 30));
    AlterColumn("dbo.Movies", "Rating", c => c.String(maxLength: 5));
}

O Genre campo não é mais anulável (ou seja, você deve inserir um valor). O Rating campo tem um comprimento máximo de 5 e Title um comprimento máximo de 60. O comprimento mínimo de 3 on Title e o intervalo on Price não criaram alterações de esquema.

Examine o esquema do filme:

Captura de tela que mostra a guia Design de filmes dbo ponto. Título e Classificação são verificados na coluna Permitir Nulos.

Os campos de cadeia de caracteres mostram os novos limites de comprimento e Genre não são mais verificados como anuláveis.

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 acima, Genre e Rating devem usar apenas letras (espaço em branco, números e caracteres especiais não são permitidos). 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 Required atributo.

O Code First garante que as regras de validação especificadas em uma classe de modelo sejam impostas antes que o aplicativo salve as alterações no banco de dados. Por exemplo, o código a seguir lançará uma exceção DbEntityValidationException quando o SaveChanges método for chamado, pois vários valores de propriedade necessários Movie estão ausentes:

MovieDBContext db = new MovieDBContext();
Movie movie = new Movie();
movie.Title = "Gone with the Wind";
db.Movies.Add(movie);
db.SaveChanges();        // <= Will throw server side validation exception

O código acima gera a seguinte exceção:

Falha na validação para uma ou mais entidades. Consulte a propriedade 'EntityValidationErrors' para obter mais detalhes.

Ter regras de validação impostas automaticamente pelo .NET Framework 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 no MVC ASP.NET

Execute o aplicativo e navegue até a URL / Movies .

Clique 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.

8_validationErrors

Observação

para dar suporte à validação do jQuery para localidades diferentes do inglês que usam uma vírgula (",") para um ponto decimal, você deve incluir o NuGet globalize conforme descrito anteriormente neste tutorial.

Observe como o formulário usou automaticamente uma cor de borda vermelha para realçar as caixas de texto que contêm dados inválidos e emitiu uma mensagem de erro de validação apropriada ao lado de cada uma. 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).

Um benefício real é que você não precisou alterar uma única linha de código na MoviesController classe 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 são enviados no servidor até que não haja erros de validação do lado do cliente. Você pode verificar isso colocando um ponto de interrupção no método HTTP Post, usando a ferramenta fiddler ou as ferramentas de desenvolvedor do IE F12.

Como a validação ocorre no modo de exibição Criar e no método de ação Criar

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. A próxima listagem mostra a aparência dos Create métodos na MovieController classe. Eles permanecem inalterados em relação à forma como você os criou anteriormente neste tutorial.

public ActionResult Create()
{
    return View();
}
// POST: /Movies/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (ModelState.IsValid)
    {
        db.Movies.Add(movie);
        db.SaveChanges();
        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 Create método (a HttpPost versão) verifica ModelState.IsValid se o filme tem algum erro de validação. Obter essa propriedade avalia todos os atributos de validação que foram aplicados ao objeto. Se o objeto tiver erros de validação, o método exibirá Create novamente o formulário. 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 Create método nunca é chamado. Se você desabilitar o JavaScript no navegador, a validação do cliente será desabilitada e o método HTTP POST Create verificará ModelState.IsValid se o filme tem algum erro 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 Internet Explorer.

Captura de tela que mostra a janela Opções da Internet aberta e na guia de segurança. A janela Nível personalizado está aberta e o script ativo está desabilitado.

A captura de tela que mostra a postagem H t t p e se o ponto de estado do modelo é válido é realçada.

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

Captura de tela que mostra a janela Opções. O conteúdo é selecionado e Ativar Java Script é circulado em vermelho.

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

Captura de tela que mostra a configuração do Java Script e a opção de permitir ou desativar.

Abaixo está o modelo de exibição Create.cshtml que você scaffolded anteriormente no tutorial. Ela é usada pelos métodos de ação mostrados acima para exibir o formulário inicial e exibi-lo novamente em caso de erro.

@model MvcMovie.Models.Movie
@{
    ViewBag.Title = "Create";
}
<h2>Create</h2>
@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()    
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        @Html.ValidationSummary(true)
        <div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>
        @*Fields removed for brevity.*@        




        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Observe como o código usa um Html.EditorFor auxiliar para gerar o <input> elemento para cada Movie propriedade. Ao lado desse auxiliar está uma chamada para o Html.ValidationMessageFor método auxiliar. Esses dois métodos auxiliares funcionam com o objeto de modelo que é passado pelo controlador para a exibição (nesse caso, um Movie objeto). Eles procuram automaticamente os atributos de validação especificados no modelo e exibem mensagens de erro conforme apropriado.

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.

Se você quiser alterar a lógica de validação posteriormente, poderá fazê-lo em exatamente um local adicionando atributos de validação ao modelo (neste exemplo, a movie classe). 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. E isso significa que você estará honrando 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.

[DataType(DataType.Date)] 
public DateTime ReleaseDate { get; set; }
       
[DataType(DataType.Currency)] 
public decimal Price { get; set; }

Os atributos DataType fornecem apenas dicas para o mecanismo de exibição formatar os dados (e fornecer atributos como <a> URLs e <a href="mailto:EmailAddress.com"> emails. Você pode usar 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 do banco de dados, eles não são atributos de validação. Nesse caso, apenas desejamos acompanhar a data, não a data e a hora. A Enumeração de Tipo de Dados fornece muitos tipos de dados, como Data, Hora, Número de Telefone, Moeda, Endereço de Email 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 mailto: link 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 HTML 5 data- (pronuncia-se data dash) que os navegadores HTML 5 podem entender. Os atributos DataType nã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 no CultureInfo do servidor.

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

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

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

Você pode usar o atributo DisplayFormat sozinho, mas geralmente é uma boa ideia usar o atributo DataType também. O DataType atributo transmite a semântica dos dados em vez de como renderizá-los em uma tela e fornece os seguintes benefícios que você não obtém comDisplayFormat:

  • O navegador pode habilitar recursos HTML5 (por exemplo, para mostrar um controle de calendário, o símbolo de moeda apropriada para a localidade, links de e-mail etc.).
  • Por padrão, o navegador renderizará os dados usando o formato correto com base em sua localidade.
  • O atributo DataType pode permitir que o MVC escolha o modelo de campo correto para renderizar os dados (o DisplayFormat, se usado sozinho, usa o modelo de cadeia de caracteres). Para obter mais informações, consulte os modelos ASP.NET MVC 2 de Brad Wilson. (Embora escrito para MVC 2, este artigo ainda se aplica à versão atual do ASP.NET MVC.)

Se você usar o DataType atributo com um campo de data, também precisará especificar o DisplayFormat atributo para garantir que o campo seja renderizado corretamente nos navegadores Chrome. Para obter mais informações, consulte este tópico do StackOverflow.

Observação

A validação do jQuery não funciona com o atributo 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 fixas em seus modelos, portanto, não é recomendável usar o atributo Range e DateTime.

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

public class Movie
{
   public int ID { get; set; }
   [Required,StringLength(60, MinimumLength = 3)]
   public string Title { get; set; }
   [Display(Name = "Release Date"),DataType(DataType.Date)]
   public DateTime ReleaseDate { get; set; }
   [Required]
   public string Genre { get; set; }
   [Range(1, 100),DataType(DataType.Currency)]
   public decimal Price { get; set; }
   [Required,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.