Eventos
Campeonato mundial de DataViz de Power BI
14 feb, 16 - 31 mar, 16
Con 4 posibilidades de entrar, podrías ganar un paquete de conferencia y convertirlo en el Live Grand Finale en Las Vegas
Saber másEste explorador ya no se admite.
Actualice a Microsoft Edge para aprovechar las características y actualizaciones de seguridad más recientes, y disponer de soporte técnico.
En este artículo se explica cómo validar la entrada del usuario en una aplicación ASP.NET Core MVC o Razor Pages.
Mira o descarga el código de ejemplo (cómo descargarlo).
El estado del modelo representa los errores que proceden de dos subsistemas: el enlace de modelos y la validación de modelos. Los errores que se originan del enlace de modelos suelen ser errores de conversión de datos. Por ejemplo, se escribe una "x" en un campo numérico entero. La validación del modelo se produce después del enlace de modelos y notifica los errores en los que los datos no cumplen las reglas de negocio. Por ejemplo, se especifica un 0 en un campo que espera una clasificación entre 1 y 5.
Tanto el enlace como la validación de modelos se producen antes de la ejecución de una acción de controlador o un método de controlador de Razor Pages. En el caso de las aplicaciones web, la aplicación es responsable de inspeccionar ModelState.IsValid
y reaccionar de manera apropiada. Normalmente, las aplicaciones web vuelven a reproducir la página con un mensaje de error, como se muestra en el ejemplo de Razor Pages siguiente:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Para ASP.NET Core MVC con controladores y vistas, en el ejemplo siguiente se muestra cómo revisar ModelState.IsValid
dentro de una acción de controlador:
public async Task<IActionResult> Create(Movie movie)
{
if (!ModelState.IsValid)
{
return View(movie);
}
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Los controladores de Web API no tienen que comprobar si ModelState.IsValid
tienen el atributo [ApiController]. En ese caso, se devuelve una respuesta HTTP 400 automática que contiene los detalles del error cuando el estado del modelo no es válido. Para obtener más información, consulte Respuestas HTTP 400 automáticas.
La validación es automática, pero tal vez le interese repetirla manualmente. Por ejemplo, tal vez haya calculado el valor de una propiedad y quiera volver a ejecutar la validación después de establecer la propiedad en el valor calculado. Para volver a ejecutar la validación, llame a ModelStateDictionary.ClearValidationState para borrar la validación específica del modelo que se está validando seguida de TryValidateModel
:
public async Task<IActionResult> OnPostTryValidateAsync()
{
var modifiedReleaseDate = DateTime.Now.Date;
Movie.ReleaseDate = modifiedReleaseDate;
ModelState.ClearValidationState(nameof(Movie));
if (!TryValidateModel(Movie, nameof(Movie)))
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Los atributos de validación permiten especificar reglas de validación para las propiedades del modelo. En el ejemplo siguiente de la aplicación de ejemplo se muestra una clase de modelo anotada con atributos de validación. El atributo [ClassicMovie]
es un atributo de validación personalizado y los demás están integrados. No se muestra [ClassicMovieWithClientValidator]
, que indica una manera alternativa de implementar un atributo personalizado.
public class Movie
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[ClassicMovie(1960)]
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
}
Estos son algunos de los atributos de validación integrados:
[Required]
para obtener más información sobre el comportamiento de este atributo.[Remote]
para obtener más información sobre el comportamiento de este atributo.En el espacio de nombres System.ComponentModel.DataAnnotations encontrará una lista completa de atributos de validación.
Los atributos de validación permiten especificar el mensaje de error que se mostrará para una entrada no válida. Por ejemplo:
[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]
Internamente, los atributos llaman a String.Format con un marcador de posición para el nombre de campo y, en ocasiones, marcadores de posición adicionales. Por ejemplo:
[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]
Cuando se aplica a una propiedad Name
, el mensaje de error creado por el código anterior sería "La longitud del nombre debe estar entre 6 y 8".
Para averiguar qué parámetros se pasan a String.Format
para el mensaje de error de un atributo determinado, vea el código fuente de DataAnnotations.
De forma predeterminada, cuando se produce un error de validación, la validación del modelo genera un ModelStateDictionary con el nombre de propiedad como la clave de error. Algunas aplicaciones, como las aplicaciones de una sola página, se benefician del uso de nombres de propiedad JSON para errores de validación generados a partir de las API web. El código siguiente configura la validación para usar SystemTextJsonValidationMetadataProvider
con el fin de emplear nombres de propiedad JSON:
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider());
});
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
El código siguiente configura la validación para usar NewtonsoftJsonValidationMetadataProvider
con el fin de emplear nombres de propiedad JSON al usar Json.NET:
using Microsoft.AspNetCore.Mvc.NewtonsoftJson;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonValidationMetadataProvider());
}).AddNewtonsoftJson();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Para obtener un ejemplo de la directiva para usar mayúsculas y minúsculas camel, consulte Program.cs
en GitHub.
El sistema de validación trata las propiedades enlazadas o los parámetros que no aceptan valores NULL como si tuvieran un atributo [Required(AllowEmptyStrings = true)]
. Al habilitar contextos Nullable
, MVC inicia implícitamente la validación de propiedades o parámetros que no aceptan valores NULL como si se hubieran atribuido con el atributo [Required(AllowEmptyStrings = true)]
. Observe el código siguiente:
public class Person
{
public string Name { get; set; }
}
Si la aplicación se creó con <Nullable>enable</Nullable>
, un valor que falta para Name
en un envío JSON o de formulario genera un error de validación. Esto puede parecer contradictorio, ya que el atributo [Required(AllowEmptyStrings = true)]
está implícito, pero se espera un comportamiento porque las cadenas vacías se convierten en null de forma predeterminada. Use un tipo de referencia que acepta valores NULL para permitir que se especifiquen valores NULL o que faltan para la propiedad Name
:
public class Person
{
public string? Name { get; set; }
}
Este comportamiento se puede deshabilitar si se configura SuppressImplicitRequiredAttributeForNonNullableReferenceTypes en Program.cs
:
builder.Services.AddControllers(
options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
En el servidor, si la propiedad es NULL, se considera que falta un valor requerido. Un campo que no acepta valores NULL siempre es válido y el mensaje de error del atributo [Required]
no se muestra nunca.
Aun así, el enlace de modelos para una propiedad que no acepta valores NULL podría fallar, lo que genera un mensaje de error como The value '' is invalid
. Para especificar un mensaje de error personalizado para la validación del lado servidor de tipos que no aceptan valores NULL, tiene las siguientes opciones:
Haga que el campo acepte valores NULL (por ejemplo, decimal?
en lugar de decimal
). Los tipos de valor Nullable<T> se tratan como tipos estándar que aceptan valores NULL.
Especifique el mensaje de error predeterminado que el enlace de modelos va a usar, como se muestra en el ejemplo siguiente:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
Para obtener más información sobre los errores de enlace de modelos para los que se pueden establecer mensajes predeterminados, vea DefaultModelBindingMessageProvider.
Las cadenas y los tipos que no aceptan valores NULL se tratan de forma diferente en el cliente, en comparación con el servidor. En el cliente:
Como se indicó anteriormente, los tipos que no aceptan valores NULL se tratan como si tuvieran un atributo [Required(AllowEmptyStrings = true)]
. Esto significa que se obtiene la validación del lado cliente incluso si no se aplica el atributo [Required(AllowEmptyStrings = true)]
. Si no usa el atributo, aparecerá un mensaje de error predeterminado. Para especificar un mensaje de error personalizado, use el atributo.
El atributo [Remote] implementa la validación del lado cliente que requiere llamar a un método en el servidor para determinar si la entrada del campo es válida. Por ejemplo, la aplicación podría tener que comprobar si un nombre de usuario ya está en uso.
Para implementar la validación remota:
Cree un método de acción para que lo llame JavaScript. El método remote de validación de jQuery espera una respuesta JSON:
true
significa que los datos de entrada son válidos.false
, undefined
o null
significan que la entrada no es válida. Muestre el mensaje de error predeterminado.A continuación encontrará un ejemplo de un método de acción que devuelve un mensaje de error personalizado:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyEmail(string email)
{
if (!_userService.VerifyEmail(email))
{
return Json($"Email {email} is already in use.");
}
return Json(true);
}
En la clase de modelo, anote la propiedad con un atributo [Remote]
que apunte al método de acción de validación, como se muestra en el ejemplo siguiente:
[Remote(action: "VerifyEmail", controller: "Users")]
public string Email { get; set; } = null!;
La validación del lado servidor también debe implementarse para los clientes que han deshabilitado JavaScript.
La propiedad AdditionalFields del atributo [Remote]
permite validar combinaciones de campos con los datos del servidor. Por ejemplo, si el modelo User
tuviera las propiedades FirstName
y LastName
, podría interesarle comprobar que ningún usuario actual tuviera ya ese par de nombres. En el siguiente ejemplo se muestra cómo usar AdditionalFields
:
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;
AdditionalFields
podría configurarse explícitamente para las cadenas "FirstName" y "LastName", pero, al usar el operador nameof, se simplifica la refactorización posterior. El método de acción para esta validación debe aceptar los argumentos firstName
y lastName
:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
if (!_userService.VerifyName(firstName, lastName))
{
return Json($"A user named {firstName} {lastName} already exists.");
}
return Json(true);
}
Cuando el usuario escribe un nombre o un apellido, JavaScript realiza una llamada remota para comprobar si ese par de nombres ya existe.
Para validar dos o más campos adicionales, proporciónelos como una lista delimitada por comas. Por ejemplo, para agregar una propiedad MiddleName
al modelo, establezca el atributo [Remote]
tal como se muestra en el ejemplo siguiente:
[Remote(action: "VerifyName", controller: "Users",
AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }
AdditionalFields
, al igual que todos los argumentos de atributo, debe ser una expresión constante. Por lo tanto, no use una cadena interpolada ni llame a Join para inicializar AdditionalFields
.
Si necesita una validación que no proporcionan los atributos integrados, puede hacer lo siguiente:
Para los escenarios que no se controlan mediante los atributos de validación integrados, puede crear atributos de validación personalizados. Cree una clase que herede de ValidationAttribute y reemplace el método IsValid.
El método IsValid
acepta un objeto denominado value, que es la entrada que se va a validar. Una sobrecarga también acepta un objeto ValidationContext, que proporciona información adicional, como la instancia del modelo creada por el enlace de modelos.
El siguiente ejemplo valida que la fecha de lanzamiento de una película del género Classic no sea posterior a un año especificado. El atributo [ClassicMovie]
:
public class ClassicMovieAttribute : ValidationAttribute
{
public ClassicMovieAttribute(int year)
=> Year = year;
public int Year { get; }
public string GetErrorMessage() =>
$"Classic movies must have a release year no later than {Year}.";
protected override ValidationResult? IsValid(
object? value, ValidationContext validationContext)
{
var movie = (Movie)validationContext.ObjectInstance;
var releaseYear = ((DateTime)value!).Year;
if (movie.Genre == Genre.Classic && releaseYear > Year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
}
La variable movie
del ejemplo anterior representa un objeto Movie
que contiene los datos del envío del formulario. Si se produce un error de validación, se devuelve un ValidationResult con un mensaje de error.
El ejemplo anterior solo funciona con tipos Movie
. Otra opción para la validación del nivel de clase consiste en implementar IValidatableObject en la clase de modelo, como se muestra en el ejemplo siguiente:
public class ValidatableMovie : IValidatableObject
{
private const int _classicYear = 1960;
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
{
yield return new ValidationResult(
$"Classic movies must have a release year no later than {_classicYear}.",
new[] { nameof(ReleaseDate) });
}
}
}
El código siguiente muestra cómo agregar un error de modelo después de examinar el modelo:
if (Contact.Name == Contact.ShortName)
{
ModelState.AddModelError("Contact.ShortName",
"Short name can't be the same as Name.");
}
El código siguiente implementa la prueba de validación en un controlador:
if (contact.Name == contact.ShortName)
{
ModelState.AddModelError(nameof(contact.ShortName),
"Short name can't be the same as Name.");
}
El código siguiente comprueba que el número de teléfono y el correo electrónico son únicos:
public async Task<IActionResult> OnPostAsync()
{
// Attach Validation Error Message to the Model on validation failure.
if (Contact.Name == Contact.ShortName)
{
ModelState.AddModelError("Contact.ShortName",
"Short name can't be the same as Name.");
}
if (_context.Contact.Any(i => i.PhoneNumber == Contact.PhoneNumber))
{
ModelState.AddModelError("Contact.PhoneNumber",
"The Phone number is already in use.");
}
if (_context.Contact.Any(i => i.Email == Contact.Email))
{
ModelState.AddModelError("Contact.Email", "The Email is already in use.");
}
if (!ModelState.IsValid || _context.Contact == null || Contact == null)
{
// if model is invalid, return the page with the model state errors.
return Page();
}
_context.Contact.Add(Contact);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
El código siguiente implementa la prueba de validación en un controlador:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,ShortName,Email,PhoneNumber")] Contact contact)
{
// Attach Validation Error Message to the Model on validation failure.
if (contact.Name == contact.ShortName)
{
ModelState.AddModelError(nameof(contact.ShortName),
"Short name can't be the same as Name.");
}
if (_context.Contact.Any(i => i.PhoneNumber == contact.PhoneNumber))
{
ModelState.AddModelError(nameof(contact.PhoneNumber),
"The Phone number is already in use.");
}
if (_context.Contact.Any(i => i.Email == contact.Email))
{
ModelState.AddModelError(nameof(contact.Email), "The Email is already in use.");
}
if (ModelState.IsValid)
{
_context.Add(contact);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(contact);
}
La comprobación de un número de teléfono o correo electrónico únicos normalmente también se realiza con la validación remota.
Fíjese en el siguiente ValidateNameAttribute
personalizado:
public class ValidateNameAttribute : ValidationAttribute
{
public ValidateNameAttribute()
{
const string defaultErrorMessage = "Error with Name";
ErrorMessage ??= defaultErrorMessage;
}
protected override ValidationResult? IsValid(object? value,
ValidationContext validationContext)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
return new ValidationResult("Name is required.");
}
if (value.ToString()!.ToLower().Contains("zz"))
{
return new ValidationResult(
FormatErrorMessage(validationContext.DisplayName));
}
return ValidationResult.Success;
}
}
En el código siguiente, se aplica el atributo personalizado [ValidateName]
:
public class Contact
{
public Guid Id { get; set; }
[ValidateName(ErrorMessage = "Name must not contain `zz`")]
public string? Name { get; set; }
public string? Email { get; set; }
public string? PhoneNumber { get; set; }
}
Cuando el modelo contiene zz
, se devuelve un nuevo ValidationResult.
Los nodos de nivel superior incluyen lo siguiente:
Los nodos de nivel superior enlazados al modelo se validan además de la validación de las propiedades del modelo. En el ejemplo siguiente de la aplicación de muestra, el método VerifyPhone
usa RegularExpressionAttribute para validar el parámetro de acción phone
:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
[RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
if (!ModelState.IsValid)
{
return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
}
return Json(true);
}
Los nodos de nivel superior pueden usar BindRequiredAttribute con atributos de validación. En el ejemplo siguiente de la aplicación de muestra, el método CheckAge
especifica que el parámetro age
debe estar enlazado desde la cadena de consulta al enviar el formulario:
[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{
En la página Comprobar edad (CheckAge.cshtml
), hay dos formularios. El primer formulario envía un valor Age
de 99
como una cadena de parámetro de consulta: https://localhost:5001/Users/CheckAge?Age=99
.
Al enviar un parámetro age
con un formato correcto desde la cadena de consulta, el formulario se valida.
El segundo formulario de la página Comprobar edad envía el valor Age
en el cuerpo de la solicitud, y se produce un error de validación. Se produce un error en el enlace porque el parámetro age
debe provenir de una cadena de consulta.
La validación se detiene cuando se alcanza el número máximo de errores (200 de forma predeterminada). Puede configurar este número con el siguiente código en Program.cs
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
ValidationVisitor recorre el gráfico de objetos del modelo que se está validando. En el caso de los modelos profundos o infinitamente recursivos, la validación podría causar un desbordamiento de pila. MvcOptions.MaxValidationDepth proporciona una manera de detener pronto la validación si la recursividad del visitante supera la profundidad configurada. El valor predeterminado de MvcOptions.MaxValidationDepth
es 32.
La validación cortocircuita (se omite) automáticamente si el gráfico de modelo no requiere validación. Entre los objetos para los que el tiempo de ejecución omite la validación se incluyen las colecciones de elementos primitivos (como byte[]
, string[]
y Dictionary<string, string>
) y gráficos de objeto complejo que no tienen los validadores.
La validación del lado cliente impide realizar el envío hasta que el formulario sea válido. El botón Enviar ejecuta JavaScript para enviar el formulario o mostrar mensajes de error.
La validación del lado cliente evita un recorrido de ida y vuelta innecesario en el servidor cuando hay errores de entrada en un formulario. Las referencias de script siguientes en _Layout.cshtml
y _ValidationScriptsPartial.cshtml
admiten la validación del lado del cliente:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.js"></script>
El script Validación discreta de jQuery es una biblioteca front-end personalizada de Microsoft que se basa en el conocido complemento Validación de jQuery. Si no usa Validación discreta de jQuery, deberá codificar la misma lógica de validación dos veces: una vez en los atributos de validación del lado servidor en las propiedades del modelo y luego en los scripts del lado cliente. En su lugar, los asistentes de etiquetas y los asistentes de HTML usan los atributos de validación y escriben metadatos de las propiedades del modelo para representar atributos data-
HTML 5 para los elementos de formulario que necesitan validación. Validación discreta de jQuery analiza los atributos data-
y pasa la lógica a la Validación de jQuery. De este modo, la lógica de validación del lado servidor se "copia" de manera eficaz en el cliente. Puede mostrar errores de validación en el cliente mediante el uso de asistentes de etiquetas, como se muestra aquí:
<div class="form-group">
<label asp-for="Movie.ReleaseDate" class="control-label"></label>
<input asp-for="Movie.ReleaseDate" class="form-control" />
<span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>
Los anteriores asistentes de etiquetas representan el siguiente código HTML:
<div class="form-group">
<label class="control-label" for="Movie_ReleaseDate">Release Date</label>
<input class="form-control" type="date" data-val="true"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
<span class="text-danger field-validation-valid"
data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>
Tenga en cuenta que los atributos data-
en los resultados HTML corresponden a los atributos de validación para la propiedad Movie.ReleaseDate
. El atributo data-val-required
contiene un mensaje de error que se muestra si el usuario no rellena el campo de fecha de estreno. La validación discreta de jQuery pasa este valor al método required() de la validación de jQuery, que luego muestra ese mensaje en el elemento <span> que lo acompaña.
La validación del tipo de datos se basa en el tipo .NET de una propiedad, a menos que lo reemplace un atributo [DataType]. Los exploradores tienen sus propios mensajes de error de predeterminados, pero el paquete de Validación discreta de jQuery Validate puede invalidar esos mensajes. Los atributos y las subclases [DataType]
, como [EmailAddress], permiten especificar el mensaje de error.
Para obtener información sobre la validación discreta, consulte este problema de GitHub.
Validación discreta de jQuery pasa los parámetros y la lógica de validación a la Validación de jQuery cuando la página se carga por primera vez. Por lo tanto, la validación no funciona automáticamente en los formularios generados dinámicamente. Para habilitar la validación, hay que indicarle a Validación discreta de jQuery que analice el formulario dinámico inmediatamente después de su creación. Por ejemplo, en el código siguiente se configura la validación del lado cliente en un formulario agregado mediante AJAX.
$.get({
url: "https://url/that/returns/a/form",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add form. " + errorThrown);
},
success: function(newFormHTML) {
var container = document.getElementById("form-container");
container.insertAdjacentHTML("beforeend", newFormHTML);
var forms = container.getElementsByTagName("form");
var newForm = forms[forms.length - 1];
$.validator.unobtrusive.parse(newForm);
}
})
El método $.validator.unobtrusive.parse()
acepta un selector de jQuery para su único argumento. Este método indica a Validación discreta de jQuery que analice los atributos data-
de formularios dentro de ese selector. Después, los valores de estos atributos se pasan al complemento de validación de jQuery.
El método $.validator.unobtrusive.parse()
funciona en todo el formulario, no en los controles individuales generados dinámicamente, como <input>
y <select/>
. Para volver a analizar el formulario, quite los datos de validación que se agregaron cuando el formulario se analizó anteriormente, como se muestra en el ejemplo siguiente:
$.get({
url: "https://url/that/returns/a/control",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add control. " + errorThrown);
},
success: function(newInputHTML) {
var form = document.getElementById("my-form");
form.insertAdjacentHTML("beforeend", newInputHTML);
$(form).removeData("validator") // Added by jQuery Validation
.removeData("unobtrusiveValidation"); // Added by jQuery Unobtrusive Validation
$.validator.unobtrusive.parse(form);
}
})
Para personalizar la validación del lado cliente, es necesario generar atributos HTML data-
que funcionen con un adaptador personalizado de validación de jQuery. El siguiente ejemplo de código de adaptador se escribió para los atributos [ClassicMovie]
y [ClassicMovieWithClientValidator]
que se introdujeron anteriormente en este artículo:
$.validator.addMethod('classicmovie', function (value, element, params) {
var genre = $(params[0]).val(), year = params[1], date = new Date(value);
// The Classic genre has a value of '0'.
if (genre && genre.length > 0 && genre[0] === '0') {
// The release date for a Classic is valid if it's no greater than the given year.
return date.getUTCFullYear() <= year;
}
return true;
});
$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
var element = $(options.form).find('select#Movie_Genre')[0];
options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
options.messages['classicmovie'] = options.message;
});
Para obtener información sobre cómo escribir adaptadores, vea la documentación de validación de jQuery.
El uso de un adaptador para un campo determinado se desencadena mediante atributos data-
que:
data-val="true"
).data-val-rulename="Error message."
).data-val-rulename-param1="value"
).En el ejemplo siguiente se muestran los atributos data-
del atributo ClassicMovie
de la aplicación de ejemplo:
<input class="form-control" type="date"
data-val="true"
data-val-classicmovie="Classic movies must have a release year no later than 1960."
data-val-classicmovie-year="1960"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
Como se indicó anteriormente, los asistentes de etiquetas y los asistentes de HTML usan información procedente de los atributos de validación para representar atributos data-
. Hay dos opciones para escribir código que dé como resultado la creación de atributos HTML data-
personalizados:
Este método para representar atributos data-
en HTML es lo que usa el atributo ClassicMovie
en la aplicación de ejemplo. Para agregar la validación de cliente mediante este método:
Crea una clase de adaptador de atributo para el atributo de validación personalizado. Deriva la clase de AttributeAdapterBase<TAttribute>. Crea un método AddValidation
que agrega atributos data-
a la salida representada, tal como se muestra en este ejemplo:
public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
{
public ClassicMovieAttributeAdapter(
ClassicMovieAttribute attribute, IStringLocalizer? stringLocalizer)
: base(attribute, stringLocalizer)
{
}
public override void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
}
public override string GetErrorMessage(ModelValidationContextBase validationContext)
=> Attribute.GetErrorMessage();
}
Crea una clase de proveedor de adaptador que implemente IValidationAttributeAdapterProvider. En el método GetAttributeAdapter, pasa el atributo personalizado al constructor del adaptador, como se muestra en este ejemplo:
public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
{
private readonly IValidationAttributeAdapterProvider baseProvider =
new ValidationAttributeAdapterProvider();
public IAttributeAdapter? GetAttributeAdapter(
ValidationAttribute attribute, IStringLocalizer? stringLocalizer)
{
if (attribute is ClassicMovieAttribute classicMovieAttribute)
{
return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
}
return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
}
}
Registra el proveedor del adaptador para la inserción de dependencias en Program.cs
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
Este método para representar atributos data-
en HTML es lo que usa el atributo ClassicMovieWithClientValidator
en la aplicación de ejemplo. Para agregar la validación de cliente mediante este método:
En el atributo de validación personalizado, implementa la interfaz IClientModelValidator y crea un método AddValidation. En el método AddValidation
, agrega atributos data-
para la validación, como se muestra en el ejemplo siguiente:
public class ClassicMovieWithClientValidatorAttribute :
ValidationAttribute, IClientModelValidator
{
public ClassicMovieWithClientValidatorAttribute(int year)
=> Year = year;
public int Year { get; }
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
var year = Year.ToString(CultureInfo.InvariantCulture);
MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
}
public string GetErrorMessage() =>
$"Classic movies must have a release year no later than {Year}.";
protected override ValidationResult? IsValid(
object? value, ValidationContext validationContext)
{
var movie = (Movie)validationContext.ObjectInstance;
var releaseYear = ((DateTime)value!).Year;
if (movie.Genre == Genre.Classic && releaseYear > Year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
El código siguiente deshabilita la validación de cliente en las Razor Pages:
builder.Services.AddRazorPages()
.AddViewOptions(options =>
{
options.HtmlHelperOptions.ClientValidationEnabled = false;
});
Otras opciones para deshabilitar la validación del lado cliente:
_ValidationScriptsPartial
en todos los archivos .cshtml
.El enfoque anterior no impedirá la validación del lado cliente de la biblioteca de clases de IdentityRazor de ASP.NET Core. Para obtener más información, consulta Scaffolding Identity en proyectos de ASP.NET Core.
Los detalles del problema no son el único formato de respuesta para describir un error de la API HTTP; sin embargo, se usan normalmente para notificar errores para las API HTTP.
El servicio de detalles del problema implementa la interfaz IProblemDetailsService, que admite la creación de detalles del problema en ASP.NET Core. El método de extensión AddProblemDetails(IServiceCollection) de IServiceCollection registra la implementación IProblemDetailsService
predeterminada.
En aplicaciones de ASP.NET Core, el middleware siguiente genera respuestas HTTP de detalles del problema cuando se llama a AddProblemDetails
, excepto cuando el encabezado HTTP de solicitud Accept
no incluye uno de los tipos de contenido admitidos por el IProblemDetailsWriter registrado (valor predeterminado: application/json
):
Accept
no incluye text/html
.En este artículo se explica cómo validar la entrada del usuario en una aplicación ASP.NET Core MVC o Razor Pages.
Mira o descarga el código de ejemplo (cómo descargarlo).
El estado del modelo representa los errores que proceden de dos subsistemas: el enlace de modelos y la validación de modelos. Los errores que se originan del enlace de modelos suelen ser errores de conversión de datos. Por ejemplo, se escribe una "x" en un campo numérico entero. La validación del modelo se produce después del enlace de modelos y notifica los errores en los que los datos no cumplen las reglas de negocio. Por ejemplo, se especifica un 0 en un campo que espera una clasificación entre 1 y 5.
Tanto el enlace como la validación de modelos se producen antes de la ejecución de una acción de controlador o un método de controlador de Razor Pages. En el caso de las aplicaciones web, la aplicación es responsable de inspeccionar ModelState.IsValid
y reaccionar de manera apropiada. Normalmente, las aplicaciones web vuelven a reproducir la página con un mensaje de error, como se muestra en el ejemplo de Razor Pages siguiente:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Para ASP.NET Core MVC con controladores y vistas, en el ejemplo siguiente se muestra cómo revisar ModelState.IsValid
dentro de una acción de controlador:
public async Task<IActionResult> Create(Movie movie)
{
if (!ModelState.IsValid)
{
return View(movie);
}
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Los controladores de Web API no tienen que comprobar si ModelState.IsValid
tienen el atributo [ApiController]. En ese caso, se devuelve una respuesta HTTP 400 automática que contiene los detalles del error cuando el estado del modelo no es válido. Para obtener más información, consulte Respuestas HTTP 400 automáticas.
La validación es automática, pero tal vez le interese repetirla manualmente. Por ejemplo, tal vez haya calculado el valor de una propiedad y quiera volver a ejecutar la validación después de establecer la propiedad en el valor calculado. Para volver a ejecutar la validación, llame a ModelStateDictionary.ClearValidationState para borrar la validación específica del modelo que se está validando seguida de TryValidateModel
:
public async Task<IActionResult> OnPostTryValidateAsync()
{
var modifiedReleaseDate = DateTime.Now.Date;
Movie.ReleaseDate = modifiedReleaseDate;
ModelState.ClearValidationState(nameof(Movie));
if (!TryValidateModel(Movie, nameof(Movie)))
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Los atributos de validación permiten especificar reglas de validación para las propiedades del modelo. En el ejemplo siguiente de la aplicación de ejemplo se muestra una clase de modelo anotada con atributos de validación. El atributo [ClassicMovie]
es un atributo de validación personalizado y los demás están integrados. No se muestra [ClassicMovieWithClientValidator]
, que indica una manera alternativa de implementar un atributo personalizado.
public class Movie
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[ClassicMovie(1960)]
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
}
Estos son algunos de los atributos de validación integrados:
[Required]
para obtener más información sobre el comportamiento de este atributo.[Remote]
para obtener más información sobre el comportamiento de este atributo.En el espacio de nombres System.ComponentModel.DataAnnotations encontrará una lista completa de atributos de validación.
Los atributos de validación permiten especificar el mensaje de error que se mostrará para una entrada no válida. Por ejemplo:
[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]
Internamente, los atributos llaman a String.Format con un marcador de posición para el nombre de campo y, en ocasiones, marcadores de posición adicionales. Por ejemplo:
[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]
Cuando se aplica a una propiedad Name
, el mensaje de error creado por el código anterior sería "La longitud del nombre debe estar entre 6 y 8".
Para averiguar qué parámetros se pasan a String.Format
para el mensaje de error de un atributo determinado, vea el código fuente de DataAnnotations.
El sistema de validación trata las propiedades enlazadas o los parámetros que no aceptan valores NULL como si tuvieran un atributo [Required(AllowEmptyStrings = true)]
. Al habilitar contextosNullable
, MVC inicia implícitamente la validación de propiedades o parámetros que no aceptan valores NULL en tipos no genéricos como si se hubieran atribuido con el atributo [Required(AllowEmptyStrings = true)]
. Observe el código siguiente:
public class Person
{
public string Name { get; set; }
}
Si la aplicación se creó con <Nullable>enable</Nullable>
, un valor que falta para Name
en un envío JSON o de formulario genera un error de validación. Use un tipo de referencia que acepta valores NULL para permitir que se especifiquen valores NULL o que faltan para la propiedad Name
:
public class Person
{
public string? Name { get; set; }
}
Este comportamiento se puede deshabilitar si se configura SuppressImplicitRequiredAttributeForNonNullableReferenceTypes en Program.cs
:
builder.Services.AddControllers(
options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
Las propiedades que no aceptan valores NULL en tipos genéricos deben incluir el atributo [Required]
cuando se requiere el tipo. En el código siguiente, TestRequired
no es necesario:
public class WeatherForecast<T>
{
public string TestRequired { get; set; } = null!;
public T? Inner { get; set; }
}
En el código siguiente, TestRequired
se marca explícitamente como obligatorio:
using System.ComponentModel.DataAnnotations;
public class WeatherForecast<T>
{
[Required]
public string TestRequired { get; set; } = null!;
public T? Inner { get; set; }
}
En el servidor, si la propiedad es NULL, se considera que falta un valor requerido. Un campo que no acepta valores NULL siempre es válido y el mensaje de error del atributo [Required]
no se muestra nunca.
Aun así, el enlace de modelos para una propiedad que no acepta valores NULL podría fallar, lo que genera un mensaje de error como The value '' is invalid
. Para especificar un mensaje de error personalizado para la validación del lado servidor de tipos que no aceptan valores NULL, tiene las siguientes opciones:
Haga que el campo acepte valores NULL (por ejemplo, decimal?
en lugar de decimal
). Los tipos de valor Nullable<T> se tratan como tipos estándar que aceptan valores NULL.
Especifique el mensaje de error predeterminado que el enlace de modelos va a usar, como se muestra en el ejemplo siguiente:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
Para obtener más información sobre los errores de enlace de modelos para los que se pueden establecer mensajes predeterminados, vea DefaultModelBindingMessageProvider.
Las cadenas y los tipos que no aceptan valores NULL se tratan de forma diferente en el cliente, en comparación con el servidor. En el cliente:
Como se indicó anteriormente, los tipos que no aceptan valores NULL se tratan como si tuvieran un atributo [Required(AllowEmptyStrings = true)]
. Esto significa que se obtiene la validación del lado cliente incluso si no se aplica el atributo [Required(AllowEmptyStrings = true)]
. Si no usa el atributo, aparecerá un mensaje de error predeterminado. Para especificar un mensaje de error personalizado, use el atributo.
El atributo [Remote] implementa la validación del lado cliente que requiere llamar a un método en el servidor para determinar si la entrada del campo es válida. Por ejemplo, la aplicación podría tener que comprobar si un nombre de usuario ya está en uso.
Para implementar la validación remota:
Cree un método de acción para que lo llame JavaScript. El método remote de validación de jQuery espera una respuesta JSON:
true
significa que los datos de entrada son válidos.false
, undefined
o null
significan que la entrada no es válida. Muestre el mensaje de error predeterminado.A continuación encontrará un ejemplo de un método de acción que devuelve un mensaje de error personalizado:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyEmail(string email)
{
if (!_userService.VerifyEmail(email))
{
return Json($"Email {email} is already in use.");
}
return Json(true);
}
En la clase de modelo, anote la propiedad con un atributo [Remote]
que apunte al método de acción de validación, como se muestra en el ejemplo siguiente:
[Remote(action: "VerifyEmail", controller: "Users")]
public string Email { get; set; } = null!;
La propiedad AdditionalFields del atributo [Remote]
permite validar combinaciones de campos con los datos del servidor. Por ejemplo, si el modelo User
tuviera las propiedades FirstName
y LastName
, podría interesarle comprobar que ningún usuario actual tuviera ya ese par de nombres. En el siguiente ejemplo se muestra cómo usar AdditionalFields
:
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;
AdditionalFields
podría configurarse explícitamente para las cadenas "FirstName" y "LastName", pero, al usar el operador nameof, se simplifica la refactorización posterior. El método de acción para esta validación debe aceptar los argumentos firstName
y lastName
:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
if (!_userService.VerifyName(firstName, lastName))
{
return Json($"A user named {firstName} {lastName} already exists.");
}
return Json(true);
}
Cuando el usuario escribe un nombre o un apellido, JavaScript realiza una llamada remota para comprobar si ese par de nombres ya existe.
Para validar dos o más campos adicionales, proporciónelos como una lista delimitada por comas. Por ejemplo, para agregar una propiedad MiddleName
al modelo, establezca el atributo [Remote]
tal como se muestra en el ejemplo siguiente:
[Remote(action: "VerifyName", controller: "Users",
AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }
AdditionalFields
, al igual que todos los argumentos de atributo, debe ser una expresión constante. Por lo tanto, no use una cadena interpolada ni llame a Join para inicializar AdditionalFields
.
Si necesita una validación que no proporcionan los atributos integrados, puede hacer lo siguiente:
Para los escenarios que no se controlan mediante los atributos de validación integrados, puede crear atributos de validación personalizados. Cree una clase que herede de ValidationAttribute y reemplace el método IsValid.
El método IsValid
acepta un objeto denominado value, que es la entrada que se va a validar. Una sobrecarga también acepta un objeto ValidationContext, que proporciona información adicional, como la instancia del modelo creada por el enlace de modelos.
El siguiente ejemplo valida que la fecha de lanzamiento de una película del género Classic no sea posterior a un año especificado. El atributo [ClassicMovie]
:
public class ClassicMovieAttribute : ValidationAttribute
{
public ClassicMovieAttribute(int year)
=> Year = year;
public int Year { get; }
public string GetErrorMessage() =>
$"Classic movies must have a release year no later than {Year}.";
protected override ValidationResult? IsValid(
object? value, ValidationContext validationContext)
{
var movie = (Movie)validationContext.ObjectInstance;
var releaseYear = ((DateTime)value!).Year;
if (movie.Genre == Genre.Classic && releaseYear > Year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
}
La variable movie
del ejemplo anterior representa un objeto Movie
que contiene los datos del envío del formulario. Si se produce un error de validación, se devuelve un ValidationResult con un mensaje de error.
El ejemplo anterior solo funciona con tipos Movie
. Otra opción para la validación del nivel de clase consiste en implementar IValidatableObject en la clase de modelo, como se muestra en el ejemplo siguiente:
public class ValidatableMovie : IValidatableObject
{
private const int _classicYear = 1960;
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
{
yield return new ValidationResult(
$"Classic movies must have a release year no later than {_classicYear}.",
new[] { nameof(ReleaseDate) });
}
}
}
Los nodos de nivel superior incluyen lo siguiente:
Los nodos de nivel superior enlazados al modelo se validan además de la validación de las propiedades del modelo. En el ejemplo siguiente de la aplicación de muestra, el método VerifyPhone
usa RegularExpressionAttribute para validar el parámetro de acción phone
:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
[RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
if (!ModelState.IsValid)
{
return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
}
return Json(true);
}
Los nodos de nivel superior pueden usar BindRequiredAttribute con atributos de validación. En el ejemplo siguiente de la aplicación de muestra, el método CheckAge
especifica que el parámetro age
debe estar enlazado desde la cadena de consulta al enviar el formulario:
[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{
En la página Comprobar edad (CheckAge.cshtml
), hay dos formularios. El primer formulario envía un valor Age
de 99
como una cadena de parámetro de consulta: https://localhost:5001/Users/CheckAge?Age=99
.
Al enviar un parámetro age
con un formato correcto desde la cadena de consulta, el formulario se valida.
El segundo formulario de la página Comprobar edad envía el valor Age
en el cuerpo de la solicitud, y se produce un error de validación. Se produce un error en el enlace porque el parámetro age
debe provenir de una cadena de consulta.
La validación se detiene cuando se alcanza el número máximo de errores (200 de forma predeterminada). Puede configurar este número con el siguiente código en Program.cs
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
ValidationVisitor recorre el gráfico de objetos del modelo que se está validando. En el caso de los modelos profundos o infinitamente recursivos, la validación podría causar un desbordamiento de pila. MvcOptions.MaxValidationDepth proporciona una manera de detener pronto la validación si la recursividad del visitante supera la profundidad configurada. El valor predeterminado de MvcOptions.MaxValidationDepth
es 32.
La validación cortocircuita (se omite) automáticamente si el gráfico de modelo no requiere validación. Entre los objetos para los que el tiempo de ejecución omite la validación se incluyen las colecciones de elementos primitivos (como byte[]
, string[]
y Dictionary<string, string>
) y gráficos de objeto complejo que no tienen los validadores.
La validación del lado cliente impide realizar el envío hasta que el formulario sea válido. El botón Enviar ejecuta JavaScript para enviar el formulario o mostrar mensajes de error.
La validación del lado cliente evita un recorrido de ida y vuelta innecesario en el servidor cuando hay errores de entrada en un formulario. Las referencias de script siguientes en _Layout.cshtml
y _ValidationScriptsPartial.cshtml
admiten la validación del lado del cliente:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.js"></script>
El script Validación discreta de jQuery es una biblioteca front-end personalizada de Microsoft que se basa en el conocido complemento Validación de jQuery. Si no usa Validación discreta de jQuery, deberá codificar la misma lógica de validación dos veces: una vez en los atributos de validación del lado servidor en las propiedades del modelo y luego en los scripts del lado cliente. En su lugar, los asistentes de etiquetas y los asistentes de HTML usan los atributos de validación y escriben metadatos de las propiedades del modelo para representar atributos data-
HTML 5 para los elementos de formulario que necesitan validación. Validación discreta de jQuery analiza los atributos data-
y pasa la lógica a la Validación de jQuery. De este modo, la lógica de validación del lado servidor se "copia" de manera eficaz en el cliente. Puede mostrar errores de validación en el cliente mediante el uso de asistentes de etiquetas, como se muestra aquí:
<div class="form-group">
<label asp-for="Movie.ReleaseDate" class="control-label"></label>
<input asp-for="Movie.ReleaseDate" class="form-control" />
<span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>
Los anteriores asistentes de etiquetas representan el siguiente código HTML:
<div class="form-group">
<label class="control-label" for="Movie_ReleaseDate">Release Date</label>
<input class="form-control" type="date" data-val="true"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
<span class="text-danger field-validation-valid"
data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>
Tenga en cuenta que los atributos data-
en los resultados HTML corresponden a los atributos de validación para la propiedad Movie.ReleaseDate
. El atributo data-val-required
contiene un mensaje de error que se muestra si el usuario no rellena el campo de fecha de estreno. La validación discreta de jQuery pasa este valor al método required() de la validación de jQuery, que luego muestra ese mensaje en el elemento <span> que lo acompaña.
La validación del tipo de datos se basa en el tipo .NET de una propiedad, a menos que lo reemplace un atributo [DataType]. Los exploradores tienen sus propios mensajes de error de predeterminados, pero el paquete de Validación discreta de jQuery Validate puede invalidar esos mensajes. Los atributos y las subclases [DataType]
, como [EmailAddress], permiten especificar el mensaje de error.
Para obtener información sobre la validación discreta, consulte este problema de GitHub.
Validación discreta de jQuery pasa los parámetros y la lógica de validación a la Validación de jQuery cuando la página se carga por primera vez. Por lo tanto, la validación no funciona automáticamente en los formularios generados dinámicamente. Para habilitar la validación, hay que indicarle a Validación discreta de jQuery que analice el formulario dinámico inmediatamente después de su creación. Por ejemplo, en el código siguiente se configura la validación del lado cliente en un formulario agregado mediante AJAX.
$.get({
url: "https://url/that/returns/a/form",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add form. " + errorThrown);
},
success: function(newFormHTML) {
var container = document.getElementById("form-container");
container.insertAdjacentHTML("beforeend", newFormHTML);
var forms = container.getElementsByTagName("form");
var newForm = forms[forms.length - 1];
$.validator.unobtrusive.parse(newForm);
}
})
El método $.validator.unobtrusive.parse()
acepta un selector de jQuery para su único argumento. Este método indica a Validación discreta de jQuery que analice los atributos data-
de formularios dentro de ese selector. Después, los valores de estos atributos se pasan al complemento de validación de jQuery.
El método $.validator.unobtrusive.parse()
funciona en todo el formulario, no en los controles individuales generados dinámicamente, como <input>
y <select/>
. Para volver a analizar el formulario, quite los datos de validación que se agregaron cuando el formulario se analizó anteriormente, como se muestra en el ejemplo siguiente:
$.get({
url: "https://url/that/returns/a/control",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add control. " + errorThrown);
},
success: function(newInputHTML) {
var form = document.getElementById("my-form");
form.insertAdjacentHTML("beforeend", newInputHTML);
$(form).removeData("validator") // Added by jQuery Validation
.removeData("unobtrusiveValidation"); // Added by jQuery Unobtrusive Validation
$.validator.unobtrusive.parse(form);
}
})
Para personalizar la validación del lado cliente, es necesario generar atributos HTML data-
que funcionen con un adaptador personalizado de validación de jQuery. El siguiente ejemplo de código de adaptador se escribió para los atributos [ClassicMovie]
y [ClassicMovieWithClientValidator]
que se introdujeron anteriormente en este artículo:
$.validator.addMethod('classicmovie', function (value, element, params) {
var genre = $(params[0]).val(), year = params[1], date = new Date(value);
// The Classic genre has a value of '0'.
if (genre && genre.length > 0 && genre[0] === '0') {
// The release date for a Classic is valid if it's no greater than the given year.
return date.getUTCFullYear() <= year;
}
return true;
});
$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
var element = $(options.form).find('select#Movie_Genre')[0];
options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
options.messages['classicmovie'] = options.message;
});
Para obtener información sobre cómo escribir adaptadores, vea la documentación de validación de jQuery.
El uso de un adaptador para un campo determinado se desencadena mediante atributos data-
que:
data-val="true"
).data-val-rulename="Error message."
).data-val-rulename-param1="value"
).En el ejemplo siguiente se muestran los atributos data-
del atributo ClassicMovie
de la aplicación de ejemplo:
<input class="form-control" type="date"
data-val="true"
data-val-classicmovie="Classic movies must have a release year no later than 1960."
data-val-classicmovie-year="1960"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
Como se indicó anteriormente, los asistentes de etiquetas y los asistentes de HTML usan información procedente de los atributos de validación para representar atributos data-
. Hay dos opciones para escribir código que dé como resultado la creación de atributos HTML data-
personalizados:
Este método para representar atributos data-
en HTML es lo que usa el atributo ClassicMovie
en la aplicación de ejemplo. Para agregar la validación de cliente mediante este método:
Crea una clase de adaptador de atributo para el atributo de validación personalizado. Deriva la clase de AttributeAdapterBase<TAttribute>. Crea un método AddValidation
que agrega atributos data-
a la salida representada, tal como se muestra en este ejemplo:
public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
{
public ClassicMovieAttributeAdapter(
ClassicMovieAttribute attribute, IStringLocalizer? stringLocalizer)
: base(attribute, stringLocalizer)
{
}
public override void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
}
public override string GetErrorMessage(ModelValidationContextBase validationContext)
=> Attribute.GetErrorMessage();
}
Crea una clase de proveedor de adaptador que implemente IValidationAttributeAdapterProvider. En el método GetAttributeAdapter, pasa el atributo personalizado al constructor del adaptador, como se muestra en este ejemplo:
public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
{
private readonly IValidationAttributeAdapterProvider baseProvider =
new ValidationAttributeAdapterProvider();
public IAttributeAdapter? GetAttributeAdapter(
ValidationAttribute attribute, IStringLocalizer? stringLocalizer)
{
if (attribute is ClassicMovieAttribute classicMovieAttribute)
{
return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
}
return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
}
}
Registra el proveedor del adaptador para la inserción de dependencias en Program.cs
:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
Este método para representar atributos data-
en HTML es lo que usa el atributo ClassicMovieWithClientValidator
en la aplicación de ejemplo. Para agregar la validación de cliente mediante este método:
En el atributo de validación personalizado, implementa la interfaz IClientModelValidator y crea un método AddValidation. En el método AddValidation
, agrega atributos data-
para la validación, como se muestra en el ejemplo siguiente:
public class ClassicMovieWithClientValidatorAttribute :
ValidationAttribute, IClientModelValidator
{
public ClassicMovieWithClientValidatorAttribute(int year)
=> Year = year;
public int Year { get; }
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
var year = Year.ToString(CultureInfo.InvariantCulture);
MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
}
public string GetErrorMessage() =>
$"Classic movies must have a release year no later than {Year}.";
protected override ValidationResult? IsValid(
object? value, ValidationContext validationContext)
{
var movie = (Movie)validationContext.ObjectInstance;
var releaseYear = ((DateTime)value!).Year;
if (movie.Genre == Genre.Classic && releaseYear > Year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
El código siguiente deshabilita la validación de cliente en las Razor Pages:
builder.Services.AddRazorPages()
.AddViewOptions(options =>
{
options.HtmlHelperOptions.ClientValidationEnabled = false;
});
Otras opciones para deshabilitar la validación del lado cliente:
_ValidationScriptsPartial
en todos los archivos .cshtml
.El enfoque anterior no impedirá la validación del lado cliente de la biblioteca de clases de IdentityRazor de ASP.NET Core. Para obtener más información, vea Scaffolding Identity en proyectos de ASP.NET Core.
En este artículo se explica cómo validar la entrada del usuario en una aplicación ASP.NET Core MVC o Razor Pages.
Mira o descarga el código de ejemplo (cómo descargarlo).
El estado del modelo representa los errores que proceden de dos subsistemas: el enlace de modelos y la validación de modelos. Los errores que se originan del enlace de modelos suelen ser errores de conversión de datos. Por ejemplo, se escribe una "x" en un campo numérico entero. La validación del modelo se produce después del enlace de modelos y notifica los errores en los que los datos no cumplen las reglas de negocio. Por ejemplo, se especifica un 0 en un campo que espera una clasificación entre 1 y 5.
Tanto el enlace como la validación de modelos se producen antes de la ejecución de una acción de controlador o un método de controlador de Razor Pages. En el caso de las aplicaciones web, la aplicación es responsable de inspeccionar ModelState.IsValid
y reaccionar de manera apropiada. Normalmente, las aplicaciones web vuelven a mostrar la página con un mensaje de error:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Los controladores de Web API no tienen que comprobar si ModelState.IsValid
tienen el atributo [ApiController]. En ese caso, se devuelve una respuesta HTTP 400 automática que contiene los detalles del error cuando el estado del modelo no es válido. Para obtener más información, consulte Respuestas HTTP 400 automáticas.
La validación es automática, pero tal vez le interese repetirla manualmente. Por ejemplo, tal vez haya calculado el valor de una propiedad y quiera volver a ejecutar la validación después de establecer la propiedad en el valor calculado. Para volver a ejecutar la validación, llame a ModelStateDictionary.ClearValidationState para borrar la validación específica del modelo que se está validando seguida de TryValidateModel
:
public async Task<IActionResult> OnPostTryValidateAsync()
{
var modifiedReleaseDate = DateTime.Now.Date;
Movie.ReleaseDate = modifiedReleaseDate;
ModelState.ClearValidationState(nameof(Movie));
if (!TryValidateModel(Movie, nameof(Movie)))
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Los atributos de validación permiten especificar reglas de validación para las propiedades del modelo. En el ejemplo siguiente de la aplicación de ejemplo se muestra una clase de modelo anotada con atributos de validación. El atributo [ClassicMovie]
es un atributo de validación personalizado y los demás están integrados. No se muestra [ClassicMovieWithClientValidator]
, que indica una manera alternativa de implementar un atributo personalizado.
public class Movie
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; }
[ClassicMovie(1960)]
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
}
Estos son algunos de los atributos de validación integrados:
[Required]
para obtener más información sobre el comportamiento de este atributo.[Remote]
para obtener más información sobre el comportamiento de este atributo.En el espacio de nombres System.ComponentModel.DataAnnotations encontrará una lista completa de atributos de validación.
Los atributos de validación permiten especificar el mensaje de error que se mostrará para una entrada no válida. Por ejemplo:
[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]
Internamente, los atributos llaman a String.Format con un marcador de posición para el nombre de campo y, en ocasiones, marcadores de posición adicionales. Por ejemplo:
[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]
Cuando se aplica a una propiedad Name
, el mensaje de error creado por el código anterior sería "La longitud del nombre debe estar entre 6 y 8".
Para averiguar qué parámetros se pasan a String.Format
para el mensaje de error de un atributo determinado, vea el código fuente de DataAnnotations.
El sistema de validación trata las propiedades enlazadas o los parámetros que no aceptan valores NULL como si tuvieran un atributo [Required(AllowEmptyStrings = true)]
. Al habilitar contextos Nullable
, MVC inicia implícitamente la validación de propiedades o parámetros que no aceptan valores NULL como si se hubieran atribuido con el atributo [Required(AllowEmptyStrings = true)]
. Observe el código siguiente:
public class Person
{
public string Name { get; set; }
}
Si la aplicación se creó con <Nullable>enable</Nullable>
, un valor que falta para Name
en un envío JSON o de formulario genera un error de validación. Use un tipo de referencia que acepta valores NULL para permitir que se especifiquen valores NULL o que faltan para la propiedad Name
:
public class Person
{
public string? Name { get; set; }
}
Este comportamiento se puede deshabilitar si se configura SuppressImplicitRequiredAttributeForNonNullableReferenceTypes en Startup.ConfigureServices
:
services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
En el servidor, si la propiedad es NULL, se considera que falta un valor requerido. Un campo que no acepta valores NULL siempre es válido y el mensaje de error del atributo [Required]
no se muestra nunca.
Aun así, el enlace de modelos para una propiedad que no acepta valores NULL podría fallar, lo que genera un mensaje de error como The value '' is invalid
. Para especificar un mensaje de error personalizado para la validación del lado servidor de tipos que no aceptan valores NULL, tiene las siguientes opciones:
Haga que el campo acepte valores NULL (por ejemplo, decimal?
en lugar de decimal
). Los tipos de valor Nullable<T> se tratan como tipos estándar que aceptan valores NULL.
Especifique el mensaje de error predeterminado que el enlace de modelos va a usar, como se muestra en el ejemplo siguiente:
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
services.AddSingleton<IValidationAttributeAdapterProvider,
CustomValidationAttributeAdapterProvider>();
Para obtener más información sobre los errores de enlace de modelos para los que se pueden establecer mensajes predeterminados, vea DefaultModelBindingMessageProvider.
Las cadenas y los tipos que no aceptan valores NULL se tratan de forma diferente en el cliente, en comparación con el servidor. En el cliente:
Como se indicó anteriormente, los tipos que no aceptan valores NULL se tratan como si tuvieran un atributo [Required(AllowEmptyStrings = true)]
. Esto significa que se obtiene la validación del lado cliente incluso si no se aplica el atributo [Required(AllowEmptyStrings = true)]
. Si no usa el atributo, aparecerá un mensaje de error predeterminado. Para especificar un mensaje de error personalizado, use el atributo.
El atributo [Remote] implementa la validación del lado cliente que requiere llamar a un método en el servidor para determinar si la entrada del campo es válida. Por ejemplo, la aplicación podría tener que comprobar si un nombre de usuario ya está en uso.
Para implementar la validación remota:
Cree un método de acción para que lo llame JavaScript. El método remote de validación de jQuery espera una respuesta JSON:
true
significa que los datos de entrada son válidos.false
, undefined
o null
significan que la entrada no es válida. Muestre el mensaje de error predeterminado.A continuación encontrará un ejemplo de un método de acción que devuelve un mensaje de error personalizado:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyEmail(string email)
{
if (!_userService.VerifyEmail(email))
{
return Json($"Email {email} is already in use.");
}
return Json(true);
}
En la clase de modelo, anote la propiedad con un atributo [Remote]
que apunte al método de acción de validación, como se muestra en el ejemplo siguiente:
[Remote(action: "VerifyEmail", controller: "Users")]
public string Email { get; set; }
La propiedad AdditionalFields del atributo [Remote]
permite validar combinaciones de campos con los datos del servidor. Por ejemplo, si el modelo User
tuviera las propiedades FirstName
y LastName
, podría interesarle comprobar que ningún usuario actual tuviera ya ese par de nombres. En el siguiente ejemplo se muestra cómo usar AdditionalFields
:
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; }
AdditionalFields
podría configurarse explícitamente para las cadenas "FirstName" y "LastName", pero, al usar el operador nameof, se simplifica la refactorización posterior. El método de acción para esta validación debe aceptar los argumentos firstName
y lastName
:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
if (!_userService.VerifyName(firstName, lastName))
{
return Json($"A user named {firstName} {lastName} already exists.");
}
return Json(true);
}
Cuando el usuario escribe un nombre o un apellido, JavaScript realiza una llamada remota para comprobar si ese par de nombres ya existe.
Para validar dos o más campos adicionales, proporciónelos como una lista delimitada por comas. Por ejemplo, para agregar una propiedad MiddleName
al modelo, establezca el atributo [Remote]
tal como se muestra en el ejemplo siguiente:
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }
AdditionalFields
, al igual que todos los argumentos de atributo, debe ser una expresión constante. Por lo tanto, no use una cadena interpolada ni llame a Join para inicializar AdditionalFields
.
Si necesita una validación que no proporcionan los atributos integrados, puede hacer lo siguiente:
Para los escenarios que no se controlan mediante los atributos de validación integrados, puede crear atributos de validación personalizados. Cree una clase que herede de ValidationAttribute y reemplace el método IsValid.
El método IsValid
acepta un objeto denominado value, que es la entrada que se va a validar. Una sobrecarga también acepta un objeto ValidationContext, que proporciona información adicional, como la instancia del modelo creada por el enlace de modelos.
El siguiente ejemplo valida que la fecha de lanzamiento de una película del género Classic no sea posterior a un año especificado. El atributo [ClassicMovie]
:
public class ClassicMovieAttribute : ValidationAttribute
{
public ClassicMovieAttribute(int year)
{
Year = year;
}
public int Year { get; }
public string GetErrorMessage() =>
$"Classic movies must have a release year no later than {Year}.";
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
var movie = (Movie)validationContext.ObjectInstance;
var releaseYear = ((DateTime)value).Year;
if (movie.Genre == Genre.Classic && releaseYear > Year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
}
La variable movie
del ejemplo anterior representa un objeto Movie
que contiene los datos del envío del formulario. Si se produce un error de validación, se devuelve un ValidationResult con un mensaje de error.
El ejemplo anterior solo funciona con tipos Movie
. Otra opción para la validación del nivel de clase consiste en implementar IValidatableObject en la clase de modelo, como se muestra en el ejemplo siguiente:
public class ValidatableMovie : IValidatableObject
{
private const int _classicYear = 1960;
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; }
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
{
yield return new ValidationResult(
$"Classic movies must have a release year no later than {_classicYear}.",
new[] { nameof(ReleaseDate) });
}
}
}
Los nodos de nivel superior incluyen lo siguiente:
Los nodos de nivel superior enlazados al modelo se validan además de la validación de las propiedades del modelo. En el ejemplo siguiente de la aplicación de muestra, el método VerifyPhone
usa RegularExpressionAttribute para validar el parámetro de acción phone
:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
[RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
if (!ModelState.IsValid)
{
return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
}
return Json(true);
}
Los nodos de nivel superior pueden usar BindRequiredAttribute con atributos de validación. En el ejemplo siguiente de la aplicación de muestra, el método CheckAge
especifica que el parámetro age
debe estar enlazado desde la cadena de consulta al enviar el formulario:
[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{
En la página Comprobar edad (CheckAge.cshtml
), hay dos formularios. El primer formulario envía un valor Age
de 99
como una cadena de parámetro de consulta: https://localhost:5001/Users/CheckAge?Age=99
.
Al enviar un parámetro age
con un formato correcto desde la cadena de consulta, el formulario se valida.
El segundo formulario de la página Comprobar edad envía el valor Age
en el cuerpo de la solicitud, y se produce un error de validación. Se produce un error en el enlace porque el parámetro age
debe provenir de una cadena de consulta.
La validación se detiene cuando se alcanza el número máximo de errores (200 de forma predeterminada). Puede configurar este número con el siguiente código en Startup.ConfigureServices
:
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
services.AddSingleton<IValidationAttributeAdapterProvider,
CustomValidationAttributeAdapterProvider>();
ValidationVisitor recorre el gráfico de objetos del modelo que se está validando. En el caso de los modelos profundos o infinitamente recursivos, la validación podría causar un desbordamiento de pila. MvcOptions.MaxValidationDepth proporciona una manera de detener pronto la validación si la recursividad del visitante supera la profundidad configurada. El valor predeterminado de MvcOptions.MaxValidationDepth
es 32.
La validación cortocircuita (se omite) automáticamente si el gráfico de modelo no requiere validación. Entre los objetos para los que el tiempo de ejecución omite la validación se incluyen las colecciones de elementos primitivos (como byte[]
, string[]
y Dictionary<string, string>
) y gráficos de objeto complejo que no tienen los validadores.
La validación del lado cliente impide realizar el envío hasta que el formulario sea válido. El botón Enviar ejecuta JavaScript para enviar el formulario o mostrar mensajes de error.
La validación del lado cliente evita un recorrido de ida y vuelta innecesario en el servidor cuando hay errores de entrada en un formulario. Las referencias de script siguientes en _Layout.cshtml
y _ValidationScriptsPartial.cshtml
admiten la validación del lado del cliente:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.js"></script>
El script Validación discreta de jQuery es una biblioteca front-end personalizada de Microsoft que se basa en el conocido complemento Validación de jQuery. Si no usa Validación discreta de jQuery, deberá codificar la misma lógica de validación dos veces: una vez en los atributos de validación del lado servidor en las propiedades del modelo y luego en los scripts del lado cliente. En su lugar, los asistentes de etiquetas y los asistentes de HTML usan los atributos de validación y escriben metadatos de las propiedades del modelo para representar atributos data-
HTML 5 para los elementos de formulario que necesitan validación. Validación discreta de jQuery analiza los atributos data-
y pasa la lógica a la Validación de jQuery. De este modo, la lógica de validación del lado servidor se "copia" de manera eficaz en el cliente. Puede mostrar errores de validación en el cliente mediante el uso de asistentes de etiquetas, como se muestra aquí:
<div class="form-group">
<label asp-for="Movie.ReleaseDate" class="control-label"></label>
<input asp-for="Movie.ReleaseDate" class="form-control" />
<span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>
Los anteriores asistentes de etiquetas representan el siguiente código HTML:
<div class="form-group">
<label class="control-label" for="Movie_ReleaseDate">Release Date</label>
<input class="form-control" type="date" data-val="true"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
<span class="text-danger field-validation-valid"
data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>
Tenga en cuenta que los atributos data-
en los resultados HTML corresponden a los atributos de validación para la propiedad Movie.ReleaseDate
. El atributo data-val-required
contiene un mensaje de error que se muestra si el usuario no rellena el campo de fecha de estreno. La validación discreta de jQuery pasa este valor al método required() de la validación de jQuery, que luego muestra ese mensaje en el elemento <span> que lo acompaña.
La validación del tipo de datos se basa en el tipo .NET de una propiedad, a menos que lo reemplace un atributo [DataType]. Los exploradores tienen sus propios mensajes de error de predeterminados, pero el paquete de Validación discreta de jQuery Validate puede invalidar esos mensajes. Los atributos y las subclases [DataType]
, como [EmailAddress], permiten especificar el mensaje de error.
Para obtener información sobre la validación discreta, consulte este problema de GitHub.
Validación discreta de jQuery pasa los parámetros y la lógica de validación a la Validación de jQuery cuando la página se carga por primera vez. Por lo tanto, la validación no funciona automáticamente en los formularios generados dinámicamente. Para habilitar la validación, hay que indicarle a Validación discreta de jQuery que analice el formulario dinámico inmediatamente después de su creación. Por ejemplo, en el código siguiente se configura la validación del lado cliente en un formulario agregado mediante AJAX.
$.get({
url: "https://url/that/returns/a/form",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add form. " + errorThrown);
},
success: function(newFormHTML) {
var container = document.getElementById("form-container");
container.insertAdjacentHTML("beforeend", newFormHTML);
var forms = container.getElementsByTagName("form");
var newForm = forms[forms.length - 1];
$.validator.unobtrusive.parse(newForm);
}
})
El método $.validator.unobtrusive.parse()
acepta un selector de jQuery para su único argumento. Este método indica a Validación discreta de jQuery que analice los atributos data-
de formularios dentro de ese selector. Después, los valores de estos atributos se pasan al complemento de validación de jQuery.
El método $.validator.unobtrusive.parse()
funciona en todo el formulario, no en los controles individuales generados dinámicamente, como <input>
y <select/>
. Para volver a analizar el formulario, quite los datos de validación que se agregaron cuando el formulario se analizó anteriormente, como se muestra en el ejemplo siguiente:
$.get({
url: "https://url/that/returns/a/control",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add control. " + errorThrown);
},
success: function(newInputHTML) {
var form = document.getElementById("my-form");
form.insertAdjacentHTML("beforeend", newInputHTML);
$(form).removeData("validator") // Added by jQuery Validation
.removeData("unobtrusiveValidation"); // Added by jQuery Unobtrusive Validation
$.validator.unobtrusive.parse(form);
}
})
Para personalizar la validación del lado cliente, es necesario generar atributos HTML data-
que funcionen con un adaptador personalizado de validación de jQuery. El siguiente ejemplo de código de adaptador se escribió para los atributos [ClassicMovie]
y [ClassicMovieWithClientValidator]
que se introdujeron anteriormente en este artículo:
$.validator.addMethod('classicmovie', function (value, element, params) {
var genre = $(params[0]).val(), year = params[1], date = new Date(value);
// The Classic genre has a value of '0'.
if (genre && genre.length > 0 && genre[0] === '0') {
// The release date for a Classic is valid if it's no greater than the given year.
return date.getUTCFullYear() <= year;
}
return true;
});
$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
var element = $(options.form).find('select#Movie_Genre')[0];
options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
options.messages['classicmovie'] = options.message;
});
Para obtener información sobre cómo escribir adaptadores, vea la documentación de validación de jQuery.
El uso de un adaptador para un campo determinado se desencadena mediante atributos data-
que:
data-val="true"
).data-val-rulename="Error message."
).data-val-rulename-param1="value"
).En el ejemplo siguiente se muestran los atributos data-
del atributo ClassicMovie
de la aplicación de ejemplo:
<input class="form-control" type="date"
data-val="true"
data-val-classicmovie="Classic movies must have a release year no later than 1960."
data-val-classicmovie-year="1960"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
Como se indicó anteriormente, los asistentes de etiquetas y los asistentes de HTML usan información procedente de los atributos de validación para representar atributos data-
. Hay dos opciones para escribir código que dé como resultado la creación de atributos HTML data-
personalizados:
Este método para representar atributos data-
en HTML es lo que usa el atributo ClassicMovie
en la aplicación de ejemplo. Para agregar la validación de cliente mediante este método:
Crea una clase de adaptador de atributo para el atributo de validación personalizado. Deriva la clase de AttributeAdapterBase<TAttribute>. Crea un método AddValidation
que agrega atributos data-
a la salida representada, tal como se muestra en este ejemplo:
public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
{
public ClassicMovieAttributeAdapter(ClassicMovieAttribute attribute,
IStringLocalizer stringLocalizer)
: base(attribute, stringLocalizer)
{
}
public override void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
}
public override string GetErrorMessage(ModelValidationContextBase validationContext) =>
Attribute.GetErrorMessage();
}
Crea una clase de proveedor de adaptador que implemente IValidationAttributeAdapterProvider. En el método GetAttributeAdapter, pasa el atributo personalizado al constructor del adaptador, como se muestra en este ejemplo:
public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
{
private readonly IValidationAttributeAdapterProvider baseProvider =
new ValidationAttributeAdapterProvider();
public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute,
IStringLocalizer stringLocalizer)
{
if (attribute is ClassicMovieAttribute classicMovieAttribute)
{
return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
}
return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
}
}
Registra el proveedor del adaptador para la inserción de dependencias en Startup.ConfigureServices
:
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
services.AddSingleton<IValidationAttributeAdapterProvider,
CustomValidationAttributeAdapterProvider>();
Este método para representar atributos data-
en HTML es lo que usa el atributo ClassicMovieWithClientValidator
en la aplicación de ejemplo. Para agregar la validación de cliente mediante este método:
En el atributo de validación personalizado, implementa la interfaz IClientModelValidator y crea un método AddValidation. En el método AddValidation
, agrega atributos data-
para la validación, como se muestra en el ejemplo siguiente:
public class ClassicMovieWithClientValidatorAttribute :
ValidationAttribute, IClientModelValidator
{
public ClassicMovieWithClientValidatorAttribute(int year)
{
Year = year;
}
public int Year { get; }
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
var year = Year.ToString(CultureInfo.InvariantCulture);
MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
}
public string GetErrorMessage() =>
$"Classic movies must have a release year no later than {Year}.";
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
var movie = (Movie)validationContext.ObjectInstance;
var releaseYear = ((DateTime)value).Year;
if (movie.Genre == Genre.Classic && releaseYear > Year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
El código siguiente deshabilita la validación de cliente en las Razor Pages:
services.AddRazorPages()
.AddViewOptions(options =>
{
options.HtmlHelperOptions.ClientValidationEnabled = false;
});
Otras opciones para deshabilitar la validación del lado cliente:
_ValidationScriptsPartial
en todos los archivos .cshtml
.El enfoque anterior no impedirá la validación del lado cliente de la biblioteca de clases de IdentityRazor de ASP.NET Core. Para obtener más información, vea Scaffolding Identity en proyectos de ASP.NET Core.
Comentarios de ASP.NET Core
ASP.NET Core es un proyecto de código abierto. Seleccione un vínculo para proporcionar comentarios:
Eventos
Campeonato mundial de DataViz de Power BI
14 feb, 16 - 31 mar, 16
Con 4 posibilidades de entrar, podrías ganar un paquete de conferencia y convertirlo en el Live Grand Finale en Las Vegas
Saber más