Compartir a través de


Parte 9. Adición de validación a una aplicación de ASP.NET Core MVC

Nota:

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.

Advertencia

Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión de .NET 9 de este artículo.

Por Rick Anderson

En esta sección:

  • Se agrega lógica de validación al modelo Movie.
  • Asegúrese de que las reglas de validación se aplican cada vez que un usuario crea o edita una película.

Respetar el principio DRY

Uno de los principios de diseño de MVC es DRY ("Una vez y solo una"). ASP.NET Core MVC le anima a que especifique la funcionalidad o el comportamiento una sola vez y a que luego los refleje en el resto de la aplicación. Esto reduce la cantidad de código que necesita escribir y hace que el código que escribe sea menos propenso a errores, así como más fácil probar y de mantener.

La compatibilidad de validación proporcionada por MVC y Entity Framework Core es un buen ejemplo del principio DRY en acción. Puede especificar las reglas de validación mediante declaración en un lugar (en la clase del modelo) y las reglas se aplican en toda la aplicación.

Eliminar los datos editados anteriormente

En el paso siguiente, se agregan reglas de validación que no permiten valores NULL. Ejecute la aplicación, vaya a /Movies/Index, elimine todas las películas enumeradas y detenga la aplicación. La aplicación usará los datos de inicialización la próxima vez que se ejecute.

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

El espacio de nombres DataAnnotations proporciona un conjunto de atributos de validación integrados que se aplican mediante declaración a una clase o propiedad. DataAnnotations también contiene atributos de formato como DataType que ayudan a aplicar formato y no proporcionan ninguna validación.

Actualice la clase Movie para aprovechar las ventajas de los atributos de validación integrados Required, StringLength, RegularExpression y Range y el atributo de formato 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; }
}

Los atributos de validación especifican el comportamiento que quiere aplicar en las propiedades del modelo al que se aplican:

  • Los atributos Required y MinimumLength indican que una propiedad debe tener un valor, pero nada impide al usuario escribir espacios en blanco para satisfacer esta validación.

  • El atributo RegularExpression se usa para limitar los caracteres que se pueden escribir. En el código anterior, "Género":

    • Solo debe usar letras.
    • La primera letra debe estar en mayúsculas. Se permiten espacios en blanco, mientras que no se admiten números ni caracteres especiales.
  • La "Clasificación" de RegularExpression:

    • Requiere que el primer carácter sea una letra mayúscula.
    • Permite caracteres especiales y números en los espacios posteriores. "PG-13" es válido para una "Clasificación", pero se produce un error en un "Género".
  • 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.

  • Los tipos de valor (como decimal, int, float, DateTime) son intrínsecamente necesarios y no necesitan el atributo [Required].

El que ASP.NET Core aplique automáticamente las reglas de validación ayuda a que su 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.

UI de error de validación

Ejecute la aplicación y navegue al controlador Movies.

Seleccione el vínculo Crear nueva para agregar una nueva película. Rellene el formulario con algunos valores no válidos. En cuanto la validación del lado cliente de jQuery detecta el problema, muestra un mensaje de error.

Formulario de vista de películas con varios errores de validación del lado cliente de jQuery

Nota

Es posible que no pueda escribir comas decimales en campos decimales. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este comentario 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.

Observe cómo el formulario presenta automáticamente un mensaje de error de validación adecuado en cada campo que contiene un valor no válido. Los errores se aplican en el lado cliente (con JavaScript y jQuery) y en el lado servidor (cuando un usuario tiene JavaScript deshabilitado).

Una ventaja importante es que no fue necesario cambiar ni 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ó en pasos anteriores de este tutorial seleccionaron automáticamente las reglas de validación que especificó mediante atributos de validación en las propiedades de la clase del modelo Movie. Pruebe la aplicación mediante el método de acción Edit y se aplicará la misma validación.

Los datos del formulario no se enviarán al servidor hasta que dejen de producirse errores de validación de cliente. Puede comprobarlo colocando un punto de interrupción en el método HTTP Post mediante la herramienta Fiddler o las herramientas de desarrollo F12.

Cómo funciona la validación

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. En el código siguiente se muestran los dos 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);
}

El primer método de acción Create (HTTP GET) muestra el formulario de creación inicial. La segunda versión ([HttpPost]) controla el envío de formulario. El segundo método Create (la versión [HttpPost]) 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. En nuestro ejemplo de película, el formulario no se publica en el servidor si se detectan errores de validación del lado cliente; cuando hay errores de validación en el lado cliente, no se llama nunca al segundo método Create. Si deshabilita JavaScript en el explorador, se deshabilita también la validación del cliente y puede probar si el método Create HTTP POST ModelState.IsValid detecta errores de validación.

Puede establecer un punto de interrupción en el método [HttpPost] Create y comprobar si nunca se llama al método. La validación del lado cliente no enviará los datos del formulario si se detectan errores de validación. Si deshabilita JavaScript en el explorador y después envía el formulario con errores, se alcanzará el punto de interrupción. Puede seguir obteniendo validación completa sin JavaScript.

En la siguiente imagen se muestra cómo deshabilitar JavaScript en el explorador Firefox.

Firefox: en la pestaña Contenido de Opciones, desactive la casilla Habilitar JavaScript.

En la siguiente imagen se muestra cómo deshabilitar JavaScript en el explorador Chrome.

Google Chrome: en la sección JavaScript de Configuración de contenido, seleccione la opción para no permitir a ningún sitio ejecutar JavaScript.

Después de deshabilitar JavaScript, publique los datos no válidos y siga los pasos del depurador.

Durante la depuración en una publicación de datos no válidos, Intellisense en ModelState.IsValid muestra que el valor es falso.

Una parte de la plantilla de visualización Create.cshtml se muestra en el marcado siguiente:

<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.*@

Los métodos de acción utilizan el marcado anterior para mostrar el formulario inicial y para volver a mostrarlo en caso de error.

El asistente de etiquetas de entrada usa los atributos DataAnnotations y genera los atributos HTML necesarios para la validación de jQuery en el lado cliente. El asistente de etiquetas de validación muestra errores de validación. Para más información, vea Introduction to model validation in ASP.NET Core MVC (Introducción a la validación de modelos en ASP.NET Core MVC).

Lo realmente bueno de este enfoque es que ni el controlador ni la plantilla de vista Create saben que las reglas de validación actuales se están aplicando ni conocen 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. Estas mismas reglas de validación se aplican automáticamente a la vista Edit y a cualquier otra vista de plantillas creada que edite el modelo.

Cuando necesite cambiar la lógica de validación, puede hacerlo exactamente en un solo lugar mediante la adición de atributos de validación al modelo (en este ejemplo, la clase Movie). 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.

Uso de atributos DataType

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

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

Los atributos DataType solo proporcionan sugerencias para que el motor de vista aplique formato a los datos (y ofrece atributos o elementos como <a> para las direcciones URL y <a href="mailto:EmailAddress.com"> para el correo electrónico). Use el atributo RegularExpression para validar el formato de los datos. El atributo DataType no es un atributo de validación, sino que se usa para especificar un tipo de datos más específico que el tipo intrínseco de la base de datos. En este caso solo queremos realizar un seguimiento de la fecha, no la hora. La enumeración DataType proporciona muchos tipos de datos, como Date (Fecha), Time (Hora), PhoneNumber (Número de teléfono), Currency (Moneda), EmailAddress (Dirección de correo electrónico), etc. El atributo DataType también puede permitir que la aplicación proporcione automáticamente características específicas del tipo. Por ejemplo, se puede crear un vínculo mailto: para DataType.EmailAddress y se puede proporcionar un selector de datos para DataType.Date en exploradores compatibles con HTML5. Los atributos DataType emiten atributos HTML 5 data- (se pronuncia con el guion) que los exploradores HTML 5 pueden comprender. Los atributos DataTypeno proporcionan ninguna validación.

DataType.Date no especifica el formato de la fecha que se muestra. De manera predeterminada, el campo de datos se muestra según los formatos predeterminados basados en el elemento CultureInfo del servidor.

El atributo DisplayFormat se usa para especificar el formato de fecha de forma explícita:

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

El valor ApplyFormatInEditMode especifica que el formato se debe aplicar también cuando el valor se muestra en un cuadro de texto para su edición. En algunos campos este comportamiento puede no ser conveniente. Por poner un ejemplo, es probable que con valores de moneda no se quiera que el símbolo de la divisa se incluya en el cuadro de texto editable.

El atributo DisplayFormat puede usarse por sí solo, pero normalmente se recomienda usar el atributo DataType. El atributo DataType transmite la semántica de los datos en contraposición a cómo se representa en una pantalla y ofrece las siguientes ventajas que no proporciona DisplayFormat:

  • El explorador puede habilitar características de HTML5 (por ejemplo, mostrar un control de calendario, el símbolo de moneda adecuado según la configuración regional, vínculos de correo electrónico, etc.).

  • De manera predeterminada, el explorador representa los datos con el formato correcto según la configuración regional.

  • El atributo DataType puede habilitar MVC para que elija la plantilla de campo adecuada para representar los datos (DisplayFormat, si se usa por sí solo, usa la plantilla de cadena).

Nota

La validación de jQuery no funciona con el atributo Range ni DateTime. Por ejemplo, el código siguiente siempre muestra un error de validación del lado cliente, incluso cuando la fecha está en el intervalo especificado:

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

Debe deshabilitar la validación de fechas de jQuery para usar el atributo Range con DateTime. Por lo general no se recomienda compilar fechas fijas en los modelos, así que desaconseja usar el atributo Range y DateTime.

El código siguiente muestra la combinación de atributos en una línea:

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

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.

Recursos adicionales

En esta sección:

  • Se agrega lógica de validación al modelo Movie.
  • Asegúrese de que las reglas de validación se aplican cada vez que un usuario crea o edita una película.

Respetar el principio DRY

Uno de los principios de diseño de MVC es DRY ("Una vez y solo una"). ASP.NET Core MVC le anima a que especifique la funcionalidad o el comportamiento una sola vez y a que luego los refleje en el resto de la aplicación. Esto reduce la cantidad de código que necesita escribir y hace que el código que escribe sea menos propenso a errores, así como más fácil probar y de mantener.

La compatibilidad de validación proporcionada por MVC y Entity Framework Core Code First es un buen ejemplo del principio DRY. Puede especificar las reglas de validación mediante declaración en un lugar (en la clase del modelo) y las reglas se aplican en toda la aplicación.

Eliminar los datos editados anteriormente

En el paso siguiente, se agregan reglas de validación que no permiten valores NULL. Ejecute la aplicación, vaya a /Movies/Index, elimine todas las películas enumeradas y detenga la aplicación. La aplicación usará los datos de inicialización la próxima vez que se ejecute.

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

El espacio de nombres DataAnnotations proporciona un conjunto de atributos de validación integrados que se aplican mediante declaración a una clase o propiedad. DataAnnotations también contiene atributos de formato como DataType que ayudan a aplicar formato y no proporcionan ninguna validación.

Actualice la clase Movie para aprovechar las ventajas de los atributos de validación integrados Required, StringLength, RegularExpression y Range y el atributo de formato 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; }
}

Los atributos de validación especifican el comportamiento que quiere aplicar en las propiedades del modelo al que se aplican:

  • Los atributos Required y MinimumLength indican que una propiedad debe tener un valor, pero nada impide al usuario escribir espacios en blanco para satisfacer esta validación.

  • El atributo RegularExpression se usa para limitar los caracteres que se pueden escribir. En el código anterior, "Género":

    • Solo debe usar letras.
    • La primera letra debe estar en mayúsculas. Se permiten espacios en blanco, mientras que no se admiten números ni caracteres especiales.
  • La "Clasificación" de RegularExpression:

    • Requiere que el primer carácter sea una letra mayúscula.
    • Permite caracteres especiales y números en los espacios posteriores. "PG-13" es válido para una "Clasificación", pero se produce un error en un "Género".
  • 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.

  • Los tipos de valor (como decimal, int, float, DateTime) son intrínsecamente necesarios y no necesitan el atributo [Required].

El que ASP.NET Core aplique automáticamente las reglas de validación ayuda a que su 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.

UI de error de validación

Ejecute la aplicación y navegue al controlador Movies.

Seleccione el vínculo Crear nueva para agregar una nueva película. Rellene el formulario con algunos valores no válidos. En cuanto la validación del lado cliente de jQuery detecta el problema, muestra un mensaje de error.

Formulario de vista de películas con varios errores de validación del lado cliente de jQuery

Nota

Es posible que no pueda escribir comas decimales en campos decimales. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este comentario 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.

Observe cómo el formulario presenta automáticamente un mensaje de error de validación adecuado en cada campo que contiene un valor no válido. Los errores se aplican en el lado cliente (con JavaScript y jQuery) y en el lado servidor (cuando un usuario tiene JavaScript deshabilitado).

Una ventaja importante es que no fue necesario cambiar ni 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ó en pasos anteriores de este tutorial seleccionaron automáticamente las reglas de validación que especificó mediante atributos de validación en las propiedades de la clase del modelo Movie. Pruebe la aplicación mediante el método de acción Edit y se aplicará la misma validación.

Los datos del formulario no se enviarán al servidor hasta que dejen de producirse errores de validación de cliente. Puede comprobarlo colocando un punto de interrupción en el método HTTP Post mediante la herramienta Fiddler o las herramientas de desarrollo F12.

Cómo funciona la validación

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. En el código siguiente se muestran los dos 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);
}

El primer método de acción Create (HTTP GET) muestra el formulario de creación inicial. La segunda versión ([HttpPost]) controla el envío de formulario. El segundo método Create (la versión [HttpPost]) 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. En nuestro ejemplo de película, el formulario no se publica en el servidor si se detectan errores de validación del lado cliente; cuando hay errores de validación en el lado cliente, no se llama nunca al segundo método Create. Si deshabilita JavaScript en el explorador, se deshabilita también la validación del cliente y puede probar si el método Create HTTP POST ModelState.IsValid detecta errores de validación.

Puede establecer un punto de interrupción en el método [HttpPost] Create y comprobar si nunca se llama al método. La validación del lado cliente no enviará los datos del formulario si se detectan errores de validación. Si deshabilita JavaScript en el explorador y después envía el formulario con errores, se alcanzará el punto de interrupción. Puede seguir obteniendo validación completa sin JavaScript.

En la siguiente imagen se muestra cómo deshabilitar JavaScript en el explorador Firefox.

Firefox: en la pestaña Contenido de Opciones, desactive la casilla Habilitar JavaScript.

En la siguiente imagen se muestra cómo deshabilitar JavaScript en el explorador Chrome.

Google Chrome: en la sección JavaScript de Configuración de contenido, seleccione la opción para no permitir a ningún sitio ejecutar JavaScript.

Después de deshabilitar JavaScript, publique los datos no válidos y siga los pasos del depurador.

Durante la depuración en una publicación de datos no válidos, Intellisense en ModelState.IsValid muestra que el valor es falso.

Una parte de la plantilla de visualización Create.cshtml se muestra en el marcado siguiente:

<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.*@

Los métodos de acción utilizan el marcado anterior para mostrar el formulario inicial y para volver a mostrarlo en caso de error.

El asistente de etiquetas de entrada usa los atributos DataAnnotations y genera los atributos HTML necesarios para la validación de jQuery en el lado cliente. El asistente de etiquetas de validación muestra errores de validación. Para más información, vea Introduction to model validation in ASP.NET Core MVC (Introducción a la validación de modelos en ASP.NET Core MVC).

Lo realmente bueno de este enfoque es que ni el controlador ni la plantilla de vista Create saben que las reglas de validación actuales se están aplicando ni conocen 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. Estas mismas reglas de validación se aplican automáticamente a la vista Edit y a cualquier otra vista de plantillas creada que edite el modelo.

Cuando necesite cambiar la lógica de validación, puede hacerlo exactamente en un solo lugar mediante la adición de atributos de validación al modelo (en este ejemplo, la clase Movie). 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.

Uso de atributos DataType

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

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

Los atributos DataType solo proporcionan sugerencias para que el motor de vista aplique formato a los datos (y ofrece atributos o elementos como <a> para las direcciones URL y <a href="mailto:EmailAddress.com"> para el correo electrónico). Use el atributo RegularExpression para validar el formato de los datos. El atributo DataType no es un atributo de validación, sino que se usa para especificar un tipo de datos más específico que el tipo intrínseco de la base de datos. En este caso solo queremos realizar un seguimiento de la fecha, no la hora. La enumeración DataType proporciona muchos tipos de datos, como Date (Fecha), Time (Hora), PhoneNumber (Número de teléfono), Currency (Moneda), EmailAddress (Dirección de correo electrónico), etc. El atributo DataType también puede permitir que la aplicación proporcione automáticamente características específicas del tipo. Por ejemplo, se puede crear un vínculo mailto: para DataType.EmailAddress y se puede proporcionar un selector de datos para DataType.Date en exploradores compatibles con HTML5. Los atributos DataType emiten atributos HTML 5 data- (se pronuncia con el guion) que los exploradores HTML 5 pueden comprender. Los atributos DataTypeno proporcionan ninguna validación.

DataType.Date no especifica el formato de la fecha que se muestra. De manera predeterminada, el campo de datos se muestra según los formatos predeterminados basados en el elemento CultureInfo del servidor.

El atributo DisplayFormat se usa para especificar el formato de fecha de forma explícita:

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

El valor ApplyFormatInEditMode especifica que el formato se debe aplicar también cuando el valor se muestra en un cuadro de texto para su edición. En algunos campos este comportamiento puede no ser conveniente. Por poner un ejemplo, es probable que con valores de moneda no se quiera que el símbolo de la divisa se incluya en el cuadro de texto editable.

El atributo DisplayFormat puede usarse por sí solo, pero normalmente se recomienda usar el atributo DataType. El atributo DataType transmite la semántica de los datos en contraposición a cómo se representa en una pantalla y ofrece las siguientes ventajas que no proporciona DisplayFormat:

  • El explorador puede habilitar características de HTML5 (por ejemplo, mostrar un control de calendario, el símbolo de moneda adecuado según la configuración regional, vínculos de correo electrónico, etc.).

  • De manera predeterminada, el explorador representa los datos con el formato correcto según la configuración regional.

  • El atributo DataType puede habilitar MVC para que elija la plantilla de campo adecuada para representar los datos (DisplayFormat, si se usa por sí solo, usa la plantilla de cadena).

Nota

La validación de jQuery no funciona con el atributo Range ni DateTime. Por ejemplo, el código siguiente siempre muestra un error de validación del lado cliente, incluso cuando la fecha está en el intervalo especificado:

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

Debe deshabilitar la validación de fechas de jQuery para usar el atributo Range con DateTime. Por lo general no se recomienda compilar fechas fijas en los modelos, así que desaconseja usar el atributo Range y DateTime.

El código siguiente muestra la combinación de atributos en una línea:

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

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.

Recursos adicionales

En esta sección:

  • Se agrega lógica de validación al modelo Movie.
  • Asegúrese de que las reglas de validación se aplican cada vez que un usuario crea o edita una película.

Respetar el principio DRY

Uno de los principios de diseño de MVC es DRY ("Una vez y solo una"). ASP.NET Core MVC le anima a que especifique la funcionalidad o el comportamiento una sola vez y a que luego los refleje en el resto de la aplicación. Esto reduce la cantidad de código que necesita escribir y hace que el código que escribe sea menos propenso a errores, así como más fácil probar y de mantener.

La compatibilidad de validación proporcionada por MVC y Entity Framework Core Code First es un buen ejemplo del principio DRY. Puede especificar las reglas de validación mediante declaración en un lugar (en la clase del modelo) y las reglas se aplican en toda la aplicación.

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

El espacio de nombres DataAnnotations proporciona un conjunto de atributos de validación integrados que se aplican mediante declaración a una clase o propiedad. DataAnnotations también contiene atributos de formato como DataType que ayudan a aplicar formato y no proporcionan ninguna validación.

Actualice la clase Movie para aprovechar las ventajas de los atributos de validación integrados Required, StringLength, RegularExpression y Range y el atributo de formato 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; }
}

Los atributos de validación especifican el comportamiento que quiere aplicar en las propiedades del modelo al que se aplican:

  • Los atributos Required y MinimumLength indican que una propiedad debe tener un valor, pero nada impide al usuario escribir espacios en blanco para satisfacer esta validación.

  • El atributo RegularExpression se usa para limitar los caracteres que se pueden escribir. En el código anterior, "Género":

    • Solo debe usar letras.
    • La primera letra debe estar en mayúsculas. Se permiten espacios en blanco, mientras que no se admiten números ni caracteres especiales.
  • La "Clasificación" de RegularExpression:

    • Requiere que el primer carácter sea una letra mayúscula.
    • Permite caracteres especiales y números en los espacios posteriores. "PG-13" es válido para una "Clasificación", pero se produce un error en un "Género".
  • 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.

  • Los tipos de valor (como decimal, int, float, DateTime) son intrínsecamente necesarios y no necesitan el atributo [Required].

El que ASP.NET Core aplique automáticamente las reglas de validación ayuda a que su 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.

UI de error de validación

Ejecute la aplicación y navegue al controlador Movies.

Seleccione el vínculo Crear nueva para agregar una nueva película. Rellene el formulario con algunos valores no válidos. En cuanto la validación del lado cliente de jQuery detecta el problema, muestra un mensaje de error.

Formulario de vista de películas con varios errores de validación del lado cliente de jQuery

Nota

Es posible que no pueda escribir comas decimales en campos decimales. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este comentario 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.

Observe cómo el formulario presenta automáticamente un mensaje de error de validación adecuado en cada campo que contiene un valor no válido. Los errores se aplican en el lado cliente (con JavaScript y jQuery) y en el lado servidor (cuando un usuario tiene JavaScript deshabilitado).

Una ventaja importante es que no fue necesario cambiar ni 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ó en pasos anteriores de este tutorial seleccionaron automáticamente las reglas de validación que especificó mediante atributos de validación en las propiedades de la clase del modelo Movie. Pruebe la aplicación mediante el método de acción Edit y se aplicará la misma validación.

Los datos del formulario no se enviarán al servidor hasta que dejen de producirse errores de validación de cliente. Puede comprobarlo colocando un punto de interrupción en el método HTTP Post mediante la herramienta Fiddler o las herramientas de desarrollo F12.

Cómo funciona la validación

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. En el código siguiente se muestran los dos 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);
}

El primer método de acción Create (HTTP GET) muestra el formulario de creación inicial. La segunda versión ([HttpPost]) controla el envío de formulario. El segundo método Create (la versión [HttpPost]) 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. En nuestro ejemplo de película, el formulario no se publica en el servidor si se detectan errores de validación del lado cliente; cuando hay errores de validación en el lado cliente, no se llama nunca al segundo método Create. Si deshabilita JavaScript en el explorador, se deshabilita también la validación del cliente y puede probar si el método Create HTTP POST ModelState.IsValid detecta errores de validación.

Puede establecer un punto de interrupción en el método [HttpPost] Create y comprobar si nunca se llama al método. La validación del lado cliente no enviará los datos del formulario si se detectan errores de validación. Si deshabilita JavaScript en el explorador y después envía el formulario con errores, se alcanzará el punto de interrupción. Puede seguir obteniendo validación completa sin JavaScript.

En la siguiente imagen se muestra cómo deshabilitar JavaScript en el explorador Firefox.

Firefox: en la pestaña Contenido de Opciones, desactive la casilla Habilitar JavaScript.

En la siguiente imagen se muestra cómo deshabilitar JavaScript en el explorador Chrome.

Google Chrome: en la sección JavaScript de Configuración de contenido, seleccione la opción para no permitir a ningún sitio ejecutar JavaScript.

Después de deshabilitar JavaScript, publique los datos no válidos y siga los pasos del depurador.

Durante la depuración en una publicación de datos no válidos, Intellisense en ModelState.IsValid muestra que el valor es falso.

Una parte de la plantilla de visualización Create.cshtml se muestra en el marcado siguiente:

<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.*@

Los métodos de acción utilizan el marcado anterior para mostrar el formulario inicial y para volver a mostrarlo en caso de error.

El asistente de etiquetas de entrada usa los atributos DataAnnotations y genera los atributos HTML necesarios para la validación de jQuery en el lado cliente. El asistente de etiquetas de validación muestra errores de validación. Para más información, vea Introduction to model validation in ASP.NET Core MVC (Introducción a la validación de modelos en ASP.NET Core MVC).

Lo realmente bueno de este enfoque es que ni el controlador ni la plantilla de vista Create saben que las reglas de validación actuales se están aplicando ni conocen 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. Estas mismas reglas de validación se aplican automáticamente a la vista Edit y a cualquier otra vista de plantillas creada que edite el modelo.

Cuando necesite cambiar la lógica de validación, puede hacerlo exactamente en un solo lugar mediante la adición de atributos de validación al modelo (en este ejemplo, la clase Movie). 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.

Uso de atributos DataType

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

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

Los atributos DataType solo proporcionan sugerencias para que el motor de vista aplique formato a los datos (y ofrece atributos o elementos como <a> para las direcciones URL y <a href="mailto:EmailAddress.com"> para el correo electrónico). Use el atributo RegularExpression para validar el formato de los datos. El atributo DataType no es un atributo de validación, sino que se usa para especificar un tipo de datos más específico que el tipo intrínseco de la base de datos. En este caso solo queremos realizar un seguimiento de la fecha, no la hora. La enumeración DataType proporciona muchos tipos de datos, como Date (Fecha), Time (Hora), PhoneNumber (Número de teléfono), Currency (Moneda), EmailAddress (Dirección de correo electrónico), etc. El atributo DataType también puede permitir que la aplicación proporcione automáticamente características específicas del tipo. Por ejemplo, se puede crear un vínculo mailto: para DataType.EmailAddress y se puede proporcionar un selector de datos para DataType.Date en exploradores compatibles con HTML5. Los atributos DataType emiten atributos HTML 5 data- (se pronuncia con el guion) que los exploradores HTML 5 pueden comprender. Los atributos DataTypeno proporcionan ninguna validación.

DataType.Date no especifica el formato de la fecha que se muestra. De manera predeterminada, el campo de datos se muestra según los formatos predeterminados basados en el elemento CultureInfo del servidor.

El atributo DisplayFormat se usa para especificar el formato de fecha de forma explícita:

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

El valor ApplyFormatInEditMode especifica que el formato se debe aplicar también cuando el valor se muestra en un cuadro de texto para su edición. En algunos campos este comportamiento puede no ser conveniente. Por poner un ejemplo, es probable que con valores de moneda no se quiera que el símbolo de la divisa se incluya en el cuadro de texto editable.

El atributo DisplayFormat puede usarse por sí solo, pero normalmente se recomienda usar el atributo DataType. El atributo DataType transmite la semántica de los datos en contraposición a cómo se representa en una pantalla y ofrece las siguientes ventajas que no proporciona DisplayFormat:

  • El explorador puede habilitar características de HTML5 (por ejemplo, mostrar un control de calendario, el símbolo de moneda adecuado según la configuración regional, vínculos de correo electrónico, etc.).

  • De manera predeterminada, el explorador representa los datos con el formato correcto según la configuración regional.

  • El atributo DataType puede habilitar MVC para que elija la plantilla de campo adecuada para representar los datos (DisplayFormat, si se usa por sí solo, usa la plantilla de cadena).

Nota

La validación de jQuery no funciona con el atributo Range ni DateTime. Por ejemplo, el código siguiente siempre muestra un error de validación del lado cliente, incluso cuando la fecha está en el intervalo especificado:

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

Debe deshabilitar la validación de fechas de jQuery para usar el atributo Range con DateTime. Por lo general no se recomienda compilar fechas fijas en los modelos, así que desaconseja usar el atributo Range y DateTime.

El código siguiente muestra la combinación de atributos en una línea:

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

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.

Recursos adicionales

En esta sección:

  • Se agrega lógica de validación al modelo Movie.
  • Asegúrese de que las reglas de validación se aplican cada vez que un usuario crea o edita una película.

Respetar el principio DRY

Uno de los principios de diseño de MVC es DRY ("Una vez y solo una"). ASP.NET Core MVC le anima a que especifique la funcionalidad o el comportamiento una sola vez y a que luego los refleje en el resto de la aplicación. Esto reduce la cantidad de código que necesita escribir y hace que el código que escribe sea menos propenso a errores, así como más fácil probar y de mantener.

La compatibilidad de validación proporcionada por MVC y Entity Framework Core Code First es un buen ejemplo del principio DRY. Puede especificar las reglas de validación mediante declaración en un lugar (en la clase del modelo) y las reglas se aplican en toda la aplicación.

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

El espacio de nombres DataAnnotations proporciona un conjunto de atributos de validación integrados que se aplican mediante declaración a una clase o propiedad. DataAnnotations también contiene atributos de formato como DataType que ayudan a aplicar formato y no proporcionan ninguna validación.

Actualice la clase Movie para aprovechar los atributos de validación integrados Required, StringLength, RegularExpression y Range.

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

Los atributos de validación especifican el comportamiento que quiere aplicar en las propiedades del modelo al que se aplican:

  • Los atributos Required y MinimumLength indican que una propiedad debe tener un valor, pero nada impide al usuario escribir espacios en blanco para satisfacer esta validación.

  • El atributo RegularExpression se usa para limitar los caracteres que se pueden escribir. En el código anterior, "Género":

    • Solo debe usar letras.
    • La primera letra debe estar en mayúsculas. Se permiten espacios en blanco, mientras que no se admiten números ni caracteres especiales.
  • La "Clasificación" de RegularExpression:

    • Requiere que el primer carácter sea una letra mayúscula.
    • Permite caracteres especiales y números en los espacios posteriores. "PG-13" es válido para una "Clasificación", pero se produce un error en un "Género".
  • 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.

  • Los tipos de valor (como decimal, int, float, DateTime) son intrínsecamente necesarios y no necesitan el atributo [Required].

El que ASP.NET Core aplique automáticamente las reglas de validación ayuda a que su 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.

UI de error de validación

Ejecute la aplicación y navegue al controlador Movies.

Seleccione el vínculo Crear nueva para agregar una nueva película. Rellene el formulario con algunos valores no válidos. En cuanto la validación del lado cliente de jQuery detecta el problema, muestra un mensaje de error.

Formulario de vista de películas con varios errores de validación del lado cliente de jQuery

Nota

Es posible que no pueda escribir comas decimales en campos decimales. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este comentario 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.

Observe cómo el formulario presenta automáticamente un mensaje de error de validación adecuado en cada campo que contiene un valor no válido. Los errores se aplican en el lado cliente (con JavaScript y jQuery) y en el lado servidor (cuando un usuario tiene JavaScript deshabilitado).

Una ventaja importante es que no fue necesario cambiar ni 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ó en pasos anteriores de este tutorial seleccionaron automáticamente las reglas de validación que especificó mediante atributos de validación en las propiedades de la clase del modelo Movie. Pruebe la aplicación mediante el método de acción Edit y se aplicará la misma validación.

Los datos del formulario no se enviarán al servidor hasta que dejen de producirse errores de validación de cliente. Puede comprobarlo colocando un punto de interrupción en el método HTTP Post mediante la herramienta Fiddler o las herramientas de desarrollo F12.

Cómo funciona la validación

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. En el código siguiente se muestran los dos 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);
}

El primer método de acción Create (HTTP GET) muestra el formulario de creación inicial. La segunda versión ([HttpPost]) controla el envío de formulario. El segundo método Create (la versión [HttpPost]) 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. En nuestro ejemplo de película, el formulario no se publica en el servidor si se detectan errores de validación del lado cliente; cuando hay errores de validación en el lado cliente, no se llama nunca al segundo método Create. Si deshabilita JavaScript en el explorador, se deshabilita también la validación del cliente y puede probar si el método Create HTTP POST ModelState.IsValid detecta errores de validación.

Puede establecer un punto de interrupción en el método [HttpPost] Create y comprobar si nunca se llama al método. La validación del lado cliente no enviará los datos del formulario si se detectan errores de validación. Si deshabilita JavaScript en el explorador y después envía el formulario con errores, se alcanzará el punto de interrupción. Puede seguir obteniendo validación completa sin JavaScript.

En la siguiente imagen se muestra cómo deshabilitar JavaScript en el explorador Firefox.

Firefox: en la pestaña Contenido de Opciones, desactive la casilla Habilitar JavaScript.

En la siguiente imagen se muestra cómo deshabilitar JavaScript en el explorador Chrome.

Google Chrome: en la sección JavaScript de Configuración de contenido, seleccione la opción para no permitir a ningún sitio ejecutar JavaScript.

Después de deshabilitar JavaScript, publique los datos no válidos y siga los pasos del depurador.

Durante la depuración en una publicación de datos no válidos, Intellisense en ModelState.IsValid muestra que el valor es falso.

Una parte de la plantilla de visualización Create.cshtml se muestra en el marcado siguiente:

<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.*@

Los métodos de acción utilizan el marcado anterior para mostrar el formulario inicial y para volver a mostrarlo en caso de error.

El asistente de etiquetas de entrada usa los atributos DataAnnotations y genera los atributos HTML necesarios para la validación de jQuery en el lado cliente. El asistente de etiquetas de validación muestra errores de validación. Para más información, vea Introduction to model validation in ASP.NET Core MVC (Introducción a la validación de modelos en ASP.NET Core MVC).

Lo realmente bueno de este enfoque es que ni el controlador ni la plantilla de vista Create saben que las reglas de validación actuales se están aplicando ni conocen 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. Estas mismas reglas de validación se aplican automáticamente a la vista Edit y a cualquier otra vista de plantillas creada que edite el modelo.

Cuando necesite cambiar la lógica de validación, puede hacerlo exactamente en un solo lugar mediante la adición de atributos de validación al modelo (en este ejemplo, la clase Movie). 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.

Uso de atributos DataType

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

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

Los atributos DataType solo proporcionan sugerencias para que el motor de vista aplique formato a los datos (y ofrece atributos o elementos como <a> para las direcciones URL y <a href="mailto:EmailAddress.com"> para el correo electrónico). Use el atributo RegularExpression para validar el formato de los datos. El atributo DataType no es un atributo de validación, sino que se usa para especificar un tipo de datos más específico que el tipo intrínseco de la base de datos. En este caso solo queremos realizar un seguimiento de la fecha, no la hora. La enumeración DataType proporciona muchos tipos de datos, como Date (Fecha), Time (Hora), PhoneNumber (Número de teléfono), Currency (Moneda), EmailAddress (Dirección de correo electrónico), etc. El atributo DataType también puede permitir que la aplicación proporcione automáticamente características específicas del tipo. Por ejemplo, se puede crear un vínculo mailto: para DataType.EmailAddress y se puede proporcionar un selector de datos para DataType.Date en exploradores compatibles con HTML5. Los atributos DataType emiten atributos HTML 5 data- (se pronuncia con el guion) que los exploradores HTML 5 pueden comprender. Los atributos DataTypeno proporcionan ninguna validación.

DataType.Date no especifica el formato de la fecha que se muestra. De manera predeterminada, el campo de datos se muestra según los formatos predeterminados basados en el elemento CultureInfo del servidor.

El atributo DisplayFormat se usa para especificar el formato de fecha de forma explícita:

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

El valor ApplyFormatInEditMode especifica que el formato se debe aplicar también cuando el valor se muestra en un cuadro de texto para su edición. En algunos campos este comportamiento puede no ser conveniente. Por poner un ejemplo, es probable que con valores de moneda no se quiera que el símbolo de la divisa se incluya en el cuadro de texto editable.

El atributo DisplayFormat puede usarse por sí solo, pero normalmente se recomienda usar el atributo DataType. El atributo DataType transmite la semántica de los datos en contraposición a cómo se representa en una pantalla y ofrece las siguientes ventajas que no proporciona DisplayFormat:

  • El explorador puede habilitar características de HTML5 (por ejemplo, mostrar un control de calendario, el símbolo de moneda adecuado según la configuración regional, vínculos de correo electrónico, etc.).

  • De manera predeterminada, el explorador representa los datos con el formato correcto según la configuración regional.

  • El atributo DataType puede habilitar MVC para que elija la plantilla de campo adecuada para representar los datos (DisplayFormat, si se usa por sí solo, usa la plantilla de cadena).

Nota

La validación de jQuery no funciona con el atributo Range ni DateTime. Por ejemplo, el código siguiente siempre muestra un error de validación del lado cliente, incluso cuando la fecha está en el intervalo especificado:

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

Debe deshabilitar la validación de fechas de jQuery para usar el atributo Range con DateTime. Por lo general no se recomienda compilar fechas fijas en los modelos, así que desaconseja usar el atributo Range y DateTime.

El código siguiente muestra la combinación de atributos en una línea:

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

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.

Recursos adicionales

En esta sección:

  • Se agrega lógica de validación al modelo Movie.
  • Asegúrese de que las reglas de validación se aplican cada vez que un usuario crea o edita una película.

Respetar el principio DRY

Uno de los principios de diseño de MVC es DRY ("Una vez y solo una"). ASP.NET Core MVC le anima a que especifique la funcionalidad o el comportamiento una sola vez y a que luego los refleje en el resto de la aplicación. Esto reduce la cantidad de código que necesita escribir y hace que el código que escribe sea menos propenso a errores, así como más fácil probar y de mantener.

La compatibilidad de validación proporcionada por MVC y Entity Framework Core Code First es un buen ejemplo del principio DRY. Puede especificar las reglas de validación mediante declaración en un lugar (en la clase del modelo) y las reglas se aplican en toda la aplicación.

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

El espacio de nombres DataAnnotations proporciona un conjunto de atributos de validación integrados que se aplican mediante declaración a una clase o propiedad. DataAnnotations también contiene atributos de formato como DataType que ayudan a aplicar formato y no proporcionan ninguna validación.

Actualice la clase Movie para aprovechar los atributos de validación integrados Required, StringLength, RegularExpression y Range.

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

Los atributos de validación especifican el comportamiento que quiere aplicar en las propiedades del modelo al que se aplican:

  • Los atributos Required y MinimumLength indican que una propiedad debe tener un valor, pero nada impide al usuario escribir espacios en blanco para satisfacer esta validación.

  • El atributo RegularExpression se usa para limitar los caracteres que se pueden escribir. En el código anterior, "Género":

    • Solo debe usar letras.
    • La primera letra debe estar en mayúsculas. Se permiten espacios en blanco, pero no se admiten números ni caracteres especiales.
  • La "Clasificación" de RegularExpression:

    • Requiere que el primer carácter sea una letra mayúscula.
    • Permite caracteres especiales y números en los espacios posteriores. "PG-13" es válido para una "Clasificación", pero se produce un error en un "Género".
  • 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.

  • Los tipos de valor (como decimal, int, float, DateTime) son intrínsecamente necesarios y no necesitan el atributo [Required].

El que ASP.NET Core aplique automáticamente las reglas de validación ayuda a que su 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.

UI de error de validación

Ejecute la aplicación y navegue al controlador Movies.

Pulse el vínculo Crear nueva para agregar una nueva película. Rellene el formulario con algunos valores no válidos. En cuanto la validación del lado cliente de jQuery detecta el problema, muestra un mensaje de error.

Formulario de vista de películas con varios errores de validación del lado cliente de jQuery

Nota

Es posible que no pueda escribir comas decimales en campos decimales. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este comentario 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.

Observe cómo el formulario presenta automáticamente un mensaje de error de validación adecuado en cada campo que contiene un valor no válido. Los errores se aplican en el lado cliente (con JavaScript y jQuery) y en el lado servidor (cuando un usuario tiene JavaScript deshabilitado).

Una ventaja importante es que no fue necesario cambiar ni 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ó en pasos anteriores de este tutorial seleccionaron automáticamente las reglas de validación que especificó mediante atributos de validación en las propiedades de la clase del modelo Movie. Pruebe la aplicación mediante el método de acción Edit y se aplicará la misma validación.

Los datos del formulario no se enviarán al servidor hasta que dejen de producirse errores de validación de cliente. Puede comprobarlo colocando un punto de interrupción en el método HTTP Post mediante la herramienta Fiddler o las herramientas de desarrollo F12.

Cómo funciona la validación

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. En el código siguiente se muestran los dos 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);
}

El primer método de acción Create (HTTP GET) muestra el formulario de creación inicial. La segunda versión ([HttpPost]) controla el envío de formulario. El segundo método Create (la versión [HttpPost]) 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. En nuestro ejemplo de película, el formulario no se publica en el servidor si se detectan errores de validación del lado cliente; cuando hay errores de validación en el lado cliente, no se llama nunca al segundo método Create. Si deshabilita JavaScript en el explorador, se deshabilita también la validación del cliente y puede probar si el método Create HTTP POST ModelState.IsValid detecta errores de validación.

Puede establecer un punto de interrupción en el método [HttpPost] Create y comprobar si nunca se llama al método. La validación del lado cliente no enviará los datos del formulario si se detectan errores de validación. Si deshabilita JavaScript en el explorador y después envía el formulario con errores, se alcanzará el punto de interrupción. Puede seguir obteniendo validación completa sin JavaScript.

En la siguiente imagen se muestra cómo deshabilitar JavaScript en el explorador Firefox.

Firefox: en la pestaña Contenido de Opciones, desactive la casilla Habilitar JavaScript.

En la siguiente imagen se muestra cómo deshabilitar JavaScript en el explorador Chrome.

Google Chrome: en la sección JavaScript de Configuración de contenido, seleccione la opción para no permitir a ningún sitio ejecutar JavaScript.

Después de deshabilitar JavaScript, publique los datos no válidos y siga los pasos del depurador.

Durante la depuración en una publicación de datos no válidos, Intellisense en ModelState.IsValid muestra que el valor es falso.

La parte de la plantilla de visualización Create.cshtml se muestra en el marcado siguiente:


<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.*@

Los métodos de acción utilizan el marcado anterior para mostrar el formulario inicial y para volver a mostrarlo en caso de error.

El asistente de etiquetas de entrada usa los atributos DataAnnotations y genera los atributos HTML necesarios para la validación de jQuery en el lado cliente. El asistente de etiquetas de validación muestra errores de validación. Para más información, vea Introduction to model validation in ASP.NET Core MVC (Introducción a la validación de modelos en ASP.NET Core MVC).

Lo realmente bueno de este enfoque es que ni el controlador ni la plantilla de vista Create saben que las reglas de validación actuales se están aplicando ni conocen 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. Estas mismas reglas de validación se aplican automáticamente a la vista Edit y a cualquier otra vista de plantillas creada que edite el modelo.

Cuando necesite cambiar la lógica de validación, puede hacerlo exactamente en un solo lugar mediante la adición de atributos de validación al modelo (en este ejemplo, la clase Movie). 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.

Uso de atributos DataType

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

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

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

Los atributos DataType solo proporcionan sugerencias para que el motor de vista aplique formato a los datos (y ofrece atributos o elementos como <a> para las direcciones URL y <a href="mailto:EmailAddress.com"> para el correo electrónico). Use el atributo RegularExpression para validar el formato de los datos. El atributo DataType no es un atributo de validación, sino que se usa para especificar un tipo de datos más específico que el tipo intrínseco de la base de datos. En este caso solo queremos realizar un seguimiento de la fecha, no la hora. La enumeración DataType proporciona muchos tipos de datos, como Date (Fecha), Time (Hora), PhoneNumber (Número de teléfono), Currency (Moneda), EmailAddress (Dirección de correo electrónico), etc. El atributo DataType también puede permitir que la aplicación proporcione automáticamente características específicas del tipo. Por ejemplo, se puede crear un vínculo mailto: para DataType.EmailAddress y se puede proporcionar un selector de datos para DataType.Date en exploradores compatibles con HTML5. Los atributos DataType emiten atributos HTML 5 data- (se pronuncia con el guion) que los exploradores HTML 5 pueden comprender. Los atributos DataTypeno proporcionan ninguna validación.

DataType.Date no especifica el formato de la fecha que se muestra. De manera predeterminada, el campo de datos se muestra según los formatos predeterminados basados en el elemento CultureInfo del servidor.

El atributo DisplayFormat se usa para especificar el formato de fecha de forma explícita:

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

El valor ApplyFormatInEditMode especifica que el formato se debe aplicar también cuando el valor se muestra en un cuadro de texto para su edición. En algunos campos este comportamiento puede no ser conveniente. Por poner un ejemplo, es probable que con valores de moneda no se quiera que el símbolo de la divisa se incluya en el cuadro de texto editable.

El atributo DisplayFormat puede usarse por sí solo, pero normalmente se recomienda usar el atributo DataType. El atributo DataType transmite la semántica de los datos en contraposición a cómo se representa en una pantalla y ofrece las siguientes ventajas que no proporciona DisplayFormat:

  • El explorador puede habilitar características de HTML5 (por ejemplo, mostrar un control de calendario, el símbolo de moneda adecuado según la configuración regional, vínculos de correo electrónico, etc.).

  • De manera predeterminada, el explorador representa los datos con el formato correcto según la configuración regional.

  • El atributo DataType puede habilitar MVC para que elija la plantilla de campo adecuada para representar los datos (DisplayFormat, si se usa por sí solo, usa la plantilla de cadena).

Nota

La validación de jQuery no funciona con el atributo Range ni DateTime. Por ejemplo, el código siguiente siempre muestra un error de validación del lado cliente, incluso cuando la fecha está en el intervalo especificado:

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

Debe deshabilitar la validación de fechas de jQuery para usar el atributo Range con DateTime. Por lo general no se recomienda compilar fechas fijas en los modelos, así que desaconseja usar el atributo Range y DateTime.

El código siguiente muestra la combinación de atributos en una línea:

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

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.

Recursos adicionales