ASP.NET Core:attributi di convalida del modello API

 C Programming >> Programmazione C >  >> Tags >> API
ASP.NET Core:attributi di convalida del modello API

È sempre una buona idea convalidare i dati che entrano nella tua API web. Ci sono due passaggi che puoi fare per proteggerti da dati non validi:

  1. Dichiara le proprietà del tuo modello con i tipi appropriati. (es:stringa, DateTime, int).
  2. Utilizza gli attributi di convalida del modello. I principali integrati sono [Required], [Range], [StringLength] e [RegularExpression].

Ecco un esempio di utilizzo degli attributi di convalida del modello:

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)

Quando arriva una richiesta, il framework fa due cose:

  • Collegamento del modello:tenta di mappare i dati della richiesta alle proprietà del modello.
  • Convalida del modello:confronta i valori del modello con gli attributi di convalida del modello.

Supponiamo che tu invii la seguente richiesta di dati non validi (boxOfficeMillions non rientra nell'intervallo specificato):

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

Esegue correttamente la fase di associazione del modello, ma trova dati non validi durante la fase di convalida del modello. Per impostazione predefinita, se stai utilizzando l'attributo [ApiController] sul tuo controller API, restituirà automaticamente un 400 – Richiesta non valida con i seguenti dettagli di errore:

{
    "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:se non stai utilizzando l'attributo [ApiController] e desideri restituire manualmente un errore, puoi ottenere le informazioni sull'errore dall'oggetto ModelState.

Come puoi vedere, genera un messaggio di errore molto specifico contenente il nome della proprietà e il motivo per cui ha fallito (che si basa sull'attributo di convalida del modello che è stato utilizzato).

In questo articolo, mostrerò esempi di come utilizzare i principali attributi di convalida del modello integrati:[Obbligatorio], [Intervallo], [StringLength] e [RegularExpression].

Sostituzione del messaggio di errore

Il messaggio di errore predefinito è generalmente abbastanza buono. Tuttavia, ogni attributo ha un parametro ErrorMessage facoltativo che consente di ignorare il messaggio di errore. Ecco un esempio:

[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)

Questo cambia il messaggio di errore per quella proprietà nella risposta all'errore:

{
    "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:puoi utilizzare i segnaposto di formato nel messaggio di errore (ad es. ErrorMessage ="{0} è richiesto"), ma non consiglio di utilizzare questa funzione poiché rende il tuo codice un po' fragile.

Attributo [Richiesto]

Utilizzare l'attributo [Obbligatorio] per verificare che una proprietà nullable abbia un valore. Non riuscirà la convalida se la proprietà è mancante, il valore è null o se è una stringa vuota. Ecco un esempio:

using System.ComponentModel.DataAnnotations;

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

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

Ora supponiamo che tu invii i seguenti dati non validi (l'id è nullo e il titolo è mancante):

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

Ciò produce la seguente risposta di errore. Nota che mostra tutti gli errori di convalida:

{
    "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)

Utilizza con proprietà nullable

L'attributo [Obbligatorio] funziona solo su proprietà nullable perché l'associazione del modello avviene prima della convalida del modello. Durante l'associazione del modello, se nei dati della richiesta manca una proprietà non nullable, la proprietà del modello viene inizializzata con il valore predefinito del tipo (es:0 per int). Quindi, quando esegue la convalida del modello, vede un valore non nullo e quindi supera la convalida.

Pertanto, se si desidera utilizzare l'attributo [Obbligatorio], assicurarsi di utilizzare tipi nullable. Ad esempio:

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)

Questo sta usando stringa perché è già nullable. Sta usando un int nullable (int? ) e un DateTime nullable (DateTime? ).

Nota:non è lo stesso che inviare un valore null per una proprietà non nullable. In tale scenario, si verifica un errore di associazione del modello.

Stringhe vuote

Le stringhe sono un caso speciale. Per impostazione predefinita, [Obbligatorio] restituirà un errore se una stringa è nulla o vuota. In altre parole, la seguente richiesta con una proprietà title vuota produrrà un errore:

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

A volte potresti voler consentire stringhe vuote come valore valido, rifiutando comunque valori nulli. Per farlo, imposta AllowEmptyStrings su true:

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

Attributo [Intervallo]

Utilizza l'attributo [Intervallo] per verificare che il valore della proprietà sia compreso tra un minimo e un massimo. Viene utilizzato principalmente per i tipi numerici (es:int, decimal), ma può essere utilizzato anche con qualsiasi tipo che implementa IComparable (es:DateTime). Mostrerò degli esempi di seguito.

Il numero è compreso tra minimo e massimo

Ecco un esempio di verifica che un valore numerico (intero in questo caso) sia compreso tra un valore minimo e massimo:

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)

Ora invia dati non validi (l'id è al di fuori dell'intervallo specificato):

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

Questo produce la seguente risposta di errore:

{
    "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 è compreso tra due date

L'attributo [Intervallo] può essere utilizzato anche con tipi non numerici. Puoi usarlo con qualsiasi tipo che implementa IComparable. Devi fornire il tipo e i valori min/max come stringhe.

Ecco un esempio di verifica che una proprietà DateTime rientri in un intervallo di date:

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)

Ora inviagli dati non validi (la data di rilascio è precedente al minimo 01-01-2000):

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

Questo produce la seguente risposta di errore:

{
    "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)

Attributi di lunghezza

Esistono tre attributi di lunghezza:[MinLength], [MaxLength] e [StringLength]. Lo scenario più comune è la necessità di impostare una lunghezza massima per una stringa. È preferibile utilizzare l'attributo [StringLength] per questo, perché ha un messaggio di errore predefinito migliore.

Ecco un esempio di utilizzo di [StringLength] per limitare la lunghezza massima di una stringa:

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:puoi impostare un intervallo di lunghezza per una stringa come questa [StringLength(100, MinimumLength =50)]. È meglio che usare due attributi:[MinLength(50)] e [MaxLength(100)].

Ora invia una richiesta con dati non validi (l'id ha più di 13 caratteri):

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

Questo produce la seguente risposta di errore:

{
    "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)

Attributo [RegularExpression]

Quando è necessario verificare che il valore di una proprietà corrisponda a un modello, un'opzione consiste nell'utilizzare l'attributo [RegularExpression]. Di solito è difficile correggere l'espressione regolare ed è molto più lenta degli approcci iterativi, quindi consiglierei quanto segue:

  • Scopri il tuo pattern regex con uno strumento (come regexstorm.net).
  • Oppure trova un modello noto e testato (cerca in regexr.com).
  • Prova a fondo. I problemi di Regex emergono come eccezioni di runtime.
  • Utilizza un attributo di convalida del modello per scopi speciali esistente, se possibile (es:[Telefono], [Indirizzo email]).
  • Scrivi il tuo attributo di convalida personalizzato (non mostrato qui).
  • Sostituisci il messaggio di errore predefinito con un esempio di input valido. Il messaggio di errore predefinito mostra il pattern regex, che è davvero ostile.

Detto questo, ecco un esempio di utilizzo dell'attributo [RegularExpression] per convalidare un modello relativamente semplice:

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)

Ora invia una richiesta con dati non validi (l'id non corrisponde allo schema):

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

Questo produce la seguente risposta di errore (con il messaggio di errore personalizzato):

{
    "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