Compartilhar via


Adicionando lógica de validação ao modelo de filme

por Rick Anderson

Observação

Uma versão atualizada deste tutorial está disponível aqui que usa ASP.NET MVC 5 e Visual Studio 2013. É mais seguro, muito mais simples de seguir e demonstra mais recursos.

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 é o 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. Adicione uma using instrução na parte superior do arquivo que faça referência ao System.ComponentModel.DataAnnotations namespace:

using System.ComponentModel.DataAnnotations;

Observe que o 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.

Agora, atualize a Movie classe para aproveitar os atributos internos Required, StringLength, e validation Range . Use o código a seguir como um exemplo de onde aplicar os atributos.

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

    [Required]
    public string Title { get; set; }

    [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; }

    [StringLength(5)]
    public string Rating { get; set; }
}

Execute o aplicativo e você receberá novamente o seguinte erro de tempo de execução:

O modelo que dá suporte ao contexto 'MovieDBContext' foi alterado desde que o banco de dados foi criado. Considere usar as Migrações do Code First para atualizar o banco de dados (https://go.microsoft.com/fwlink/?LinkId=238269).

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 AddDataAnnotationsMig
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 (AddDataAnnotationsMig) e, no Up método, você poderá ver o código que atualiza as restrições de esquema. Os Title campos and Genre não são mais anuláveis (ou seja, você deve inserir um valor) e o Rating campo tem um comprimento máximo de 5.

Os atributos de validação especificam o comportamento que você deseja impor nas propriedades de modelo às quais eles são aplicados. O Required atributo indica que uma propriedade deve ter um valor; neste exemplo, um filme deve ter valores para as Titlepropriedades , ReleaseDate, Genre, e Price para ser válido. 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. Tipos intrínsecos (como decimal, int, float, DateTime) são necessários por padrão 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 abaixo lançará uma exceção quando o SaveChanges método for chamado, pois vários valores de propriedade necessários Movie estão ausentes e o preço é zero (que está fora do intervalo válido).

MovieDBContext db = new MovieDBContext();

Movie movie = new Movie();
movie.Title = "Gone with the Wind";
movie.Price = 0.0M;

db.Movies.Add(movie);  
db.SaveChanges();        // <= Will throw server side validation exception  

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.

Aqui está uma lista de código completa para o arquivo Movie.cs atualizado:

using System;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;

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

        [Required]
        public string Title { get; set; }

        [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; }

        [StringLength(5)]
        public string Rating { get; set; }
    }

    public class MovieDBContext : DbContext {
        public DbSet<Movie> Movies { get; set; }
    }
}

Interface do usuário de erro de validação no MVC ASP.NET

Execute novamente 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 e clique no botão Criar .

8_validationErrors

Observação

para oferecer suporte à validação do jQuery para localidades diferentes do inglês que usam uma vírgula (",") para um ponto decimal, você deve incluir globalize.js e suas culturas/globalize.cultures.js arquivo específico (de https://github.com/jquery/globalize ) e JavaScript para usar Globalize.parseFloat. O código a seguir mostra as modificações no arquivo Views\Movies\Edit.cshtml para trabalhar com a cultura "fr-FR":

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    <script src="~/Scripts/globalize.js"></script>
    <script src="~/Scripts/globalize.culture.fr-FR.js"></script>
    <script>
        $.validator.methods.number = function (value, element) {
            return this.optional(element) ||
                !isNaN(Globalize.parseFloat(value));
        }
        $(document).ready(function () {
            Globalize.culture('fr-FR');
        });
    </script>
    <script>
        jQuery.extend(jQuery.validator.methods, {    
            range: function (value, element, param) {        
                //Use the Globalization plugin to parse the value        
                var val = $.global.parseFloat(value);
                return this.optional(element) || (
                    val >= param[0] && val <= param[1]);
            }
        });

    </script>
}

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.

Você deve ter notado que para as propriedades Title e Genre, o atributo required não é aplicado até que você envie o formulário (clique no botão Criar ) ou insira texto no campo de entrada e o remova. Para um campo que está inicialmente vazio (como os campos na visualização Criar) e que tem apenas o atributo necessário e nenhum outro atributo de validação, você pode fazer o seguinte para disparar a validação:

  1. Tab no campo.
  2. Digite algum texto.
  3. Saída da guia.
  4. Volte para o campo.
  5. Remova o texto.
  6. Saída da guia.

A sequência acima acionará a validação necessária sem pressionar o botão enviar. Basta pressionar o botão enviar sem inserir nenhum dos campos para acionar a validação do lado do cliente. 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 testar isso colocando um ponto de interrupção no método HTTP Post ou usando a ferramenta fiddler ou as ferramentas de desenvolvedor do IE 9 F12.

Captura de tela que mostra a página Criação de filme MVC. Um alerta ao lado de Título informa que o campo Título é obrigatório. Um alerta ao lado de Gênero informa que o campo Gênero é obrigatório.

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.

//
// GET: /Movies/Create

public ActionResult Create()
{
    return View();
}

//
// POST: /Movies/Create

[HttpPost]
public ActionResult Create(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 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 que estamos usando, o formulário não é postado no servidor quando há erros de validação detectados no lado do cliente; o segundo Createmétodo nunca é chamado. Se você desabilitar o JavaScript no navegador, a validação do cliente será desabilitada e o método HTTP POST Create será chamado ModelState.IsValid para verificar 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 na guia de segurança. O nível personalizado está circulado em vermelho. Na janela Configurações de segurança, o script ativo está definido como desabilitado. A barra de rolagem está circulada em vermelho.

Captura de tela que mostra a postagem H t t p. Se o ponto Estado do Modelo for Válido for realçado.

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

Captura de tela que mostra a janela Opções. Conteúdo é selecionado e Ativar Java Script é marcado.

A imagem a seguir mostra como desativar o JavaScript com o navegador Chrome.

Captura de tela que mostra a página Opções. Under the Hood é selecionado e circulado em vermelho. Nas Configurações de conteúdo, Java Script é definido como Permitir que todos os sites executem Java Script.

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>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"></script>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Movie</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.ReleaseDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.ReleaseDate)
            @Html.ValidationMessageFor(model => model.ReleaseDate)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Genre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Genre)
            @Html.ValidationMessageFor(model => model.Genre)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Price)
            @Html.ValidationMessageFor(model => model.Price)
        </div>
        <div class="editor-label">
    @Html.LabelFor(model => model.Rating)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.Rating)
    @Html.ValidationMessageFor(model => model.Rating)
</div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

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 bom nessa abordagem é que nem o controlador nem o modelo de exibição Criar sabem nada sobre as regras de validação reais que estão sendo impostas ou sobre 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 à visualização Editar e a quaisquer outros modelos de vistas que você possa criar que editem seu 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. Além disso, isso significa que você respeitará totalmente o princípio DRY.

Adicionando formatação ao modelo de filme

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 DisplayFormat apropriado.

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

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

Os DataType atributos não são atributos de validação, eles são usados para informar ao mecanismo de exibição como renderizar o HTML. No exemplo acima, o DataType.Date atributo exibe as datas do filme apenas como datas, sem hora. Por exemplo, os seguintes DataType atributos não validam o formato dos dados:

[DataType(DataType.EmailAddress)]
[DataType(DataType.PhoneNumber)]
[DataType(DataType.Url)]

Os atributos listados acima fornecem apenas dicas para o mecanismo de visualização formatar os dados (e fornecem atributos como <um> URL for e <um href="mailto:EmailAddress.com"> para email. Você pode usar o atributo RegularExpression para validar o formato dos dados.

Uma abordagem alternativa para usar os DataType atributos, você pode definir explicitamente um DataFormatString valor. O código a seguir mostra a propriedade de data de lançamento com uma cadeia de caracteres de formato de data (ou seja, "d"). Você usaria isso para especificar que não deseja o tempo como parte da data de lançamento.

[DisplayFormat(DataFormatString = "{0:d}")]
public DateTime ReleaseDate { get; set; }

A classe completa Movie é mostrada abaixo.

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

    [Required]
    public string Title { get; set; }

    [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; }

    [StringLength(5)]
    public string Rating { get; set; }
}

Execute o aplicativo e navegue até o Movies controlador. A data de lançamento e o preço estão bem formatados. A imagem abaixo mostra a data de lançamento e o preço usando "fr-FR" como cultura.

8_format_SM

A imagem abaixo mostra os mesmos dados exibidos com a cultura padrão (inglês dos EUA).

Captura de tela que mostra a página do Índice de Filmes MVC com quatro filmes listados.

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