ASP.NET Core:atributos de validación del modelo API

 C Programming >> Programación C >  >> Tags >> API
ASP.NET Core:atributos de validación del modelo API

Siempre es una buena idea validar los datos que ingresan a su API web. Hay dos pasos que puede seguir para protegerse contra datos no válidos:

  1. Declare las propiedades de su modelo con los tipos adecuados. (por ejemplo:cadena, fecha y hora, int).
  2. Utilice atributos de validación del modelo. Los principales incorporados son [Obligatorio], [Rango], [StringLength] y [RegularExpression].

Este es un ejemplo del uso de atributos de validación del modelo:

using System.ComponentModel.DataAnnotations;

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

	[Required]
	[Range(0.0, 5000.0)]
	public decimal? BoxOfficeMillions { get; set; }

	[Required]
	public DateTime? ReleaseDate { get; set; }
}
Code language: C# (cs)

Cuando llega una solicitud, el marco hace dos cosas:

  • Enlace del modelo:intenta asignar los datos de la solicitud a las propiedades del modelo.
  • Validación del modelo:compara los valores del modelo con los atributos de validación del modelo.

Supongamos que envía la siguiente solicitud de datos no válidos (boxOfficeMillions está fuera del rango especificado):

{
    "title": "The Matrix",
    "releaseDate":"1999-03-31",
    "boxOfficeMillions": -1
}
Code language: JSON / JSON with Comments (json)

Realiza con éxito el paso de vinculación del modelo, pero luego encuentra datos no válidos durante el paso de validación del modelo. De forma predeterminada, si está utilizando el atributo [ApiController] en su controlador API, automáticamente devolverá un 400 – Solicitud incorrecta con los siguientes detalles de error:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "0HMHEO7OR788U:00000002",
    "errors": {
        "BoxOfficeMillions": [
            "The field BoxOfficeMillions must be between 0 and 5000."
        ]
    }
}
Code language: JSON / JSON with Comments (json)

Nota:si no está utilizando el atributo [ApiController] y desea devolver manualmente un error, puede obtener la información del error del objeto ModelState.

Como puede ver, genera un mensaje de error muy específico que contiene el nombre de la propiedad y por qué falló (que se basa en el atributo de validación del modelo que se usó).

En este artículo, mostraré ejemplos de cómo usar los principales atributos de validación del modelo integrado:[Obligatorio], [Rango], [Longitud de la cadena] y [Expresión regular].

Anulación del mensaje de error

El mensaje de error predeterminado suele ser lo suficientemente bueno. Sin embargo, cada atributo tiene un parámetro ErrorMessage opcional que le permite anular el mensaje de error. He aquí un ejemplo:

[Required]
[Range(0.0, 5000.0, ErrorMessage = "BoxOfficeMillions must be between $0 and $5000 million ($5 billion)" )]
public decimal? BoxOfficeMillions { get; set; }
Code language: C# (cs)

Esto cambia el mensaje de error de esa propiedad en la respuesta de error:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "0HMHEOPKUJ2L4:00000001",
    "errors": {
        "BoxOfficeMillions": [
            "BoxOfficeMillions must be between $0 and $5000 million ($5 billion)"
        ]
    }
}
Code language: JSON / JSON with Comments (json)

Nota:puede usar marcadores de posición de formato en el mensaje de error (es decir, ErrorMessage ="Se requiere {0}"), pero no recomiendo usar esa característica ya que hace que su código sea un poco frágil.

Atributo [Requerido]

Utilice el atributo [Obligatorio] para verificar que una propiedad anulable tiene un valor. Fallará la validación si falta la propiedad, si el valor es nulo o si es una cadena vacía. He aquí un ejemplo:

using System.ComponentModel.DataAnnotations;

public class Movie
{
	[Required]
	public int? Id { get; set; }

	[Required]
	public string Title { get; set; }
}
Code language: C# (cs)

Ahora supongamos que envía los siguientes datos no válidos (la identificación es nula y falta el título):

{
    "id":null
}Code language: JSON / JSON with Comments (json)

Esto produce la siguiente respuesta de error. Observe que muestra todos los errores de validación:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "0HMHEP1U0GPL4:00000001",
    "errors": {
        "Id": [
            "The Id field is required."
        ],
        "Title": [
            "The Title field is required."
        ]
    }
}
Code language: JSON / JSON with Comments (json)

Usar con propiedades anulables

El atributo [Obligatorio] solo funciona en propiedades anulables porque el enlace del modelo ocurre antes de la validación del modelo. Durante el enlace del modelo, si falta una propiedad que no acepta valores NULL en los datos de la solicitud, la propiedad del modelo se inicializa con el valor predeterminado del tipo (por ejemplo, 0 para int). Por lo tanto, cuando realiza la validación del modelo, ve un valor no nulo y, por lo tanto, pasa la validación.

Por lo tanto, si desea utilizar el atributo [Obligatorio], asegúrese de utilizar tipos que aceptan valores NULL. Por ejemplo:

using System.ComponentModel.DataAnnotations;

public class Movie
{
	[Required]
	public int? Id { get; set; }

	[Required]
	public string Title { get; set; }
	
	[Required]
	public DateTime? ReleaseDate { get; set; }
}
Code language: C# (cs)

Esto está usando cadena porque ya es anulable. Está usando un int anulable (int? ) y un DateTime anulable (DateTime? ).

Nota:esto no es lo mismo que enviar un valor nulo para una propiedad que no admite valores nulos. En ese escenario, da como resultado un error de vinculación del modelo.

Cadenas vacías

Las cadenas son un caso especial. De forma predeterminada, [Obligatorio] devolverá un error si una cadena es nula o está vacía. En otras palabras, la siguiente solicitud con una propiedad de título vacía producirá un error:

{
    "title":"",
    "id":1
}Code language: JSON / JSON with Comments (json)

A veces, es posible que desee permitir cadenas vacías como un valor válido y, al mismo tiempo, rechazar valores nulos. Para ello, establezca AllowEmptyStrings en verdadero:

[Required(AllowEmptyStrings = true)]
public string Title { get; set; }
Code language: C# (cs)

Atributo [Rango]

Use el atributo [Rango] para verificar que el valor de la propiedad esté entre un mínimo y un máximo. Esto se usa principalmente para tipos numéricos (por ejemplo, int, decimal), pero también se puede usar con cualquier tipo que implemente IComparable (por ejemplo, DateTime). Mostraré ejemplos a continuación.

El número está entre el mínimo y el máximo

Aquí hay un ejemplo de verificación de que un valor numérico (entero en este caso) está entre un valor mínimo y máximo:

using System.ComponentModel.DataAnnotations;

public class Movie
{
	[Required]
	[Range(1, 10000)]
	public int? Id { get; set; }

	[Required]
	public string Title { get; set; }
}
Code language: C# (cs)

Ahora envíe datos no válidos (la identificación está fuera del rango especificado):

{
    "title":"The Matrix",
    "id":0
}
Code language: JSON / JSON with Comments (json)

Esto produce la siguiente respuesta de error:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "0HMHEQ01EL6O8:00000001",
    "errors": {
        "Id": [
            "The field Id must be between 1 and 10000."
        ]
    }
}
Code language: JSON / JSON with Comments (json)

DateTime está entre dos fechas

El atributo [Rango] también se puede usar con tipos no numéricos. Puede usarlo con cualquier tipo que implemente IComparable. Debe proporcionar el tipo y los valores mínimos/máximos como cadenas.

Aquí hay un ejemplo de verificación de que una propiedad DateTime está dentro de un rango de fechas:

using System.ComponentModel.DataAnnotations;

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

	[Required]
	[Range(typeof(DateTime), minimum: "2000-01-01", maximum: "2050-01-01" )]
	public DateTime? ReleaseDate { get; set; }
}
Code language: C# (cs)

Ahora envíele datos no válidos (la fecha de publicación es anterior al mínimo del 2000-01-01):

{
    "title":"The Matrix",
    "releaseDate":"1999-03-31"
}
Code language: JSON / JSON with Comments (json)

Esto produce la siguiente respuesta de error:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "0HMHEQ4UNU9LI:00000003",
    "errors": {
        "ReleaseDate": [
            "The field ReleaseDate must be between 1/1/2000 12:00:00 AM and 1/1/2050 12:00:00 AM."
        ]
    }
}
Code language: JSON / JSON with Comments (json)

Atributos de longitud

Hay tres atributos de longitud:[MinLength], [MaxLength] y [StringLength]. El escenario más común es la necesidad de establecer una longitud máxima para una cadena. Es preferible usar el atributo [StringLength] para esto, porque tiene un mensaje de error predeterminado mejor.

Aquí hay un ejemplo del uso de [StringLength] para limitar la longitud máxima de una cadena:

using System.ComponentModel.DataAnnotations;
	
public class Movie
{
	[Required]
	[StringLength(13)]
	public string Id { get; set; }

	[Required]
	public string Title { get; set; }
}
Code language: C# (cs)

Nota:puede establecer un rango de longitud para una cadena como esta [StringLength(100, MinimumLength =50)]. Esto es mejor que tener que usar dos atributos:[MinLength(50)] y [MaxLength(100)].

Ahora envíe una solicitud con datos no válidos (la identificación tiene más de 13 caracteres):

{
    "title":"The Matrix",
    "id":"12345678901234"
}
Code language: JSON / JSON with Comments (json)

Esto produce la siguiente respuesta de error:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "0HMHEQJFQO9SK:00000001",
    "errors": {
        "Id": [
            "The field Id must be a string with a maximum length of 13."
        ]
    }
}
Code language: JSON / JSON with Comments (json)

Atributo [Expresión Regular]

Cuando necesite validar que el valor de una propiedad coincide con un patrón, una opción es usar el atributo [RegularExpression]. Regex suele ser difícil de corregir y es mucho más lento que los enfoques iterativos, por lo que recomendaría lo siguiente:

  • Descubra su patrón de expresiones regulares con una herramienta (como regexstorm.net).
  • O encuentre un patrón conocido y probado (busque regexr.com).
  • Prueba minuciosamente. Los problemas de expresiones regulares aparecen como excepciones de tiempo de ejecución.
  • Use un atributo de validación de modelo de propósito especial existente si es posible (por ejemplo:[Teléfono], [Dirección de correo electrónico]).
  • Escriba su propio atributo de validación personalizado (no se muestra aquí).
  • Anule el mensaje de error predeterminado con un ejemplo de entrada válida. El mensaje de error predeterminado muestra el patrón de expresión regular, que es realmente desagradable.

Dicho esto, aquí hay un ejemplo del uso del atributo [RegularExpression] para validar un patrón relativamente simple:

using System.ComponentModel.DataAnnotations;

public class Movie
{
	[Required]
	[RegularExpression("[A-Z]{3}[0-9]{3}", ErrorMessage = "Doesn't match pattern. Valid example: ABC123")]
	public string Id { get; set; }

	[Required]
	public string Title { get; set; }
}
Code language: C# (cs)

Ahora envíe una solicitud con datos no válidos (la identificación no coincide con el patrón):

{
    "title":"The Matrix",
    "id":"123"
}
Code language: JSON / JSON with Comments (json)

Esto produce la siguiente respuesta de error (con el mensaje de error personalizado):

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "0HMHEQP307PDU:00000004",
    "errors": {
        "Id": [
            "Doesn't match pattern. Valid example: ABC123"
        ]
    }
}
Code language: JSON / JSON with Comments (json)

No