Agregar la validación al modelo (C#)

por Rick Anderson

Note

Hay disponible una versión actualizada de este tutorial que usa ASP.NET MVC 5 y Visual Studio 2013. Es más seguro, mucho más fácil de seguir y demuestra más características.

Este tutorial le enseñará los aspectos básicos de la creación de una aplicación web MVC de ASP.NET con Microsoft Visual Web Developer 2010 Express Service Pack 1, que es una versión gratuita de Microsoft Visual Studio. Antes de empezar, asegúrese de que ha instalado los requisitos previos que se enumeran a continuación. Para instalar todos ellos, haga clic en el vínculo siguiente: instalador de plataforma web. Como alternativa, puede instalar individualmente los requisitos previos con los siguientes vínculos:

Si utiliza Visual Studio 2010 en lugar de Visual Web Developer 2010, instale los requisitos previos haciendo clic en el siguiente vínculo: requisitos previos de Visual Studio 2010.

Hay disponible un proyecto de Visual C# Web Developer con código fuente para acompañar este tema. Descargue la C# versión. Si prefiere Visual Basic, cambie a la versión Visual Basic de este tutorial.

En esta sección, agregará la lógica de validación al modelo de Movie y se asegurará de que las reglas de validación se apliquen siempre que un usuario intente crear o editar una película mediante la aplicación.

Mantener todo seco

Uno de los principios de diseño principales de ASP.NET MVC está seco ("Una vez y solo una"). ASP.NET MVC le anima a que especifique la funcionalidad o el comportamiento solo una vez y, a continuación, haga que se refleje en todas partes en una aplicación. Esto reduce la cantidad de código que necesita escribir y hace que el código que se escribe sea mucho más fácil de mantener.

La compatibilidad de validación proporcionada por ASP.NET MVC y Entity Framework Code First es un buen ejemplo del principio seco en acción. Puede especificar mediante declaración las reglas de validación en un solo lugar (en la clase de modelo) y, a continuación, esas reglas se aplican en todas partes de la aplicación.

Echemos un vistazo a cómo puede aprovechar esta compatibilidad de validación en la aplicación de películas.

Agregar reglas de validación al modelo de película

Empezará agregando una lógica de validación a la clase Movie.

Abra el archivo Movie.cs. Agregue una instrucción using en la parte superior del archivo que hace referencia al espacio de nombres System.ComponentModel.DataAnnotations :

using System.ComponentModel.DataAnnotations;

El espacio de nombres forma parte de la .NET Framework. Proporciona un conjunto integrado de atributos de validación que puede aplicar mediante declaración a cualquier clase o propiedad.

Ahora, actualice la clase Movie para aprovechar los atributos de validación Required, StringLengthy Range integrados. Use el código siguiente como ejemplo de dónde aplicar los atributos.

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

    [Required(ErrorMessage = "Title is required")]
    public string Title { get; set; }

    [Required(ErrorMessage = "Date is required")]
    public DateTime ReleaseDate { get; set; }

    [Required(ErrorMessage = "Genre must be specified")]
    public string Genre { get; set; }

    [Range(1, 100, ErrorMessage = "Price must be between $1 and $100")]
    public decimal Price { get; set; }

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

Los atributos de validación especifican el comportamiento que quiere aplicar en las propiedades del modelo al que se aplican. El atributo Required indica que una propiedad debe tener un valor; en este ejemplo, una película tiene que tener valores para las propiedades Title, ReleaseDate, Genrey Price para ser válidos. El atributo Range restringe un valor a un intervalo determinado. El atributo StringLength permite establecer la longitud máxima de una propiedad de cadena y, opcionalmente, su longitud mínima.

Code First garantiza que las reglas de validación que especifique en una clase de modelo se aplican antes de que la aplicación guarde los cambios en la base de datos. Por ejemplo, el código siguiente producirá una excepción cuando se llame al método SaveChanges, porque faltan algunos valores de propiedad Movie obligatorios y el precio es cero (que está fuera del 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 validation exception

El hecho de que las reglas de validación las aplique automáticamente el .NET Framework ayuda a que la aplicación sea más sólida. También nos permite asegurarnos de que todo se valida y que no nos dejamos ningún dato incorrecto en la base de datos accidentalmente.

Esta es una lista de código completa para el archivo Movie.CS actualizado:

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

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

        [Required(ErrorMessage = "Title is required")]
        public string Title { get; set; }

        public DateTime ReleaseDate { get; set; }

        [Required(ErrorMessage = "Genre must be specified")]
        public string Genre { get; set; }

        [Range(1, 100, ErrorMessage = "Price must be between $1 and $100")]
        public decimal Price { get; set; }

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

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

Interfaz de usuario de error de validación en ASP.NET MVC

Vuelva a ejecutar la aplicación y vaya a la dirección URL de /Movies .

Haga clic en el vínculo crear película para agregar una nueva película. Rellene el formulario con algunos valores no válidos y, a continuación, haga clic en el botón crear .

8_validationErrors

Observe cómo el formulario ha utilizado automáticamente un color de fondo para resaltar los cuadros de texto que contienen datos no válidos y ha emitido un mensaje de error de validación adecuado junto a cada uno de ellos. Los mensajes de error coinciden con las cadenas de error especificadas al anotar la clase Movie. Los errores se aplican tanto en el lado cliente (mediante JavaScript) como en el lado servidor (en caso de que un usuario tenga JavaScript deshabilitado).

Una ventaja real es que no necesitaba cambiar una sola línea de código en la clase MoviesController o en la vista Create. cshtml para habilitar esta interfaz de usuario de validación. El controlador y las vistas que creó anteriormente en este tutorial seleccionaron automáticamente las reglas de validación que especificó mediante atributos en la clase de modelo Movie.

Cómo se produce la validación en el método Create View y Create Action

Tal vez se pregunte cómo se generó la validación de la interfaz de usuario sin actualizar el código en el controlador o las vistas. La siguiente lista muestra el aspecto de los métodos de Create en la clase MovieController. No se modifican de la forma en que se crearon anteriormente en este 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);
}

El primer método de acción muestra el formulario de creación inicial. El segundo controla el envío del formulario. El segundo método Create llama a ModelState.IsValid para comprobar si la película tiene errores de validación. Al llamar a este método se evalúan todos los atributos de validación que se hayan aplicado al objeto. Si el objeto tiene errores de validación, el método Create vuelve a mostrar el formulario. Si no hay ningún error, el método guarda la nueva película en la base de datos.

A continuación se muestra la plantilla de vista Create. cshtml que se ha aplicado con scaffolding anteriormente en el tutorial. Los métodos de acción que se muestran arriba la usan para mostrar el formulario inicial y para volver a mostrarlo en caso de error.

@model MvcMovie.Models.Movie
@{
    ViewBag.Title = "Create";
}
<h2>
    Create</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></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 cómo el código utiliza una aplicación auxiliar de Html.EditorFor para generar el elemento <input> para cada propiedad Movie. Junto a esta aplicación auxiliar es una llamada al método auxiliar Html.ValidationMessageFor. Estos dos métodos auxiliares funcionan con el objeto de modelo que pasa el controlador a la vista (en este caso, un objeto Movie). Buscan automáticamente los atributos de validación especificados en el modelo y muestran los mensajes de error según corresponda.

Lo que es muy útil para este enfoque es que ni el controlador ni la plantilla de creación de vistas saben nada sobre las reglas de validación reales que se aplican o sobre los mensajes de error específicos que se muestran. Las reglas de validación y las cadenas de error solo se especifican en la clase Movie.

Si desea cambiar la lógica de validación más adelante, puede hacerlo en exactamente un lugar. No tendrá que preocuparse de que diferentes partes de la aplicación sean incoherentes con el modo en que se aplican las reglas: toda la lógica de validación se definirá en un solo lugar y se usará en todas partes. Esto mantiene el código muy limpio y hace que sea fácil de mantener y evolucionar. También significa que respeta totalmente el principio DRY.

Agregar formato al modelo de película

Abra el archivo Movie.cs. El espacio de nombres System.ComponentModel.DataAnnotations proporciona atributos de formato además del conjunto integrado de atributos de validación. Aplicará el DisplayFormat atributo y un valor de enumeración de DataType a la fecha de lanzamiento y a los campos de precio. En el código siguiente se muestran las propiedades ReleaseDate y Price con el atributo DisplayFormat adecuado.

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

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

Como alternativa, puede establecer explícitamente un valor de DataFormatString . En el código siguiente se muestra la propiedad Date Release con una cadena de formato de fecha (es decir, "d"). Se usaría para especificar que no desea tiempo como parte de la fecha de lanzamiento.

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

En el código siguiente se da formato a la propiedad Price como Currency.

[DisplayFormat(DataFormatString = "{0:c}")]
public decimal Price { get; set; }

A continuación se muestra la clase Movie completa.

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

    [Required(ErrorMessage = "Title is required")]
    public string Title { get; set; }

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

    [Required(ErrorMessage = "Genre must be specified")]
    public string Genre { get; set; }

    [Range(1, 100, ErrorMessage = "Price must be between $1 and $100")]
    [DisplayFormat(DataFormatString = "{0:c}")]
    public decimal Price { get; set; }

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

Ejecute la aplicación y vaya al controlador de Movies.

8_format_SM

En la siguiente parte de la serie de tutoriales, revisaremos la aplicación y realizaremos algunas mejoras a los métodos Details y Delete generados automáticamente.