Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Validazione dei dati

Validare i dati come con ASP.NET MVC, cioè utilizzando gli attributi di validazione o Data Annotation
Validare i dati come con ASP.NET MVC, cioè utilizzando gli attributi di validazione o Data Annotation
Link copiato negli appunti

Un aspetto interessante del framework, comune tra l'altro con ASP.NET MVC, è l'utilizzo del Default Model Binder per il mapping delle richieste HTTP su oggetti server side. Tra le conseguenze dirette di ciò abbiamo la possibilità di validare i dati in maniera analoga a come può essere fatto con ASP.NET MVC, cioè utilizzando gli attributi di validazione o Data Annotations.

Vediamo come fare implementando un esempio concreto che ci consenta di validare i dati prima del salvataggio di una nuova voce del nostro glossario.

Per prima cosa occorre assicurarci di aver aggiunto nel nostro progetto un riferimento a System.ComponentModel.DataAnnotations:

Figura 08. (clic per ingrandire)


Modifichiamo quindi la classe Item come mostrato di seguito:

public class Item
{
	[Required(ErrorMessage = "Il campo Termine è obbligatorio!")]
	[StringLength(20, ErrorMessage = "Il campo Termine non può superare i 20 caratteri!")]
	public string term { get; set; }
	[Required(ErrorMessage = "Il campo Definizione è obbligatorio!")]
	[StringLength(200, ErrorMessage = "Il campo Definizione non può superare i 200 caratteri!")]
	public string definition { get; set; }
	[Required(ErrorMessage = "Il campo Categoria è obbligatorio!")]
	[StringLength(20, ErrorMessage = "Il campo Categoria non può superare i 20 caratteri!")]
	public string category { get; set; }
}

Abbiamo aggiunto a ciascuna proprietà gli attributi di validazione:

  • Required, per indicare che è obbligatorio specificare un valore,
  • StringLength, per il controllo della lunghezza della stringa,

specificando i rispettivi messaggi di errore da segnalare eventualmente al client. Per gli altri possibili attributi rimandiamo alla documentazione ufficiale.

Gli attributi così specificati dichiarano quali vincoli devono essere rispettati perchè la voce del glossario sia considerata valida. La verifica di questi vincoli viene effettuata in corrispondenza del salvataggio dell'oggetto. Andiamo quindi a modificare il metodo PostItem() del nostro controller:

public HttpResponseMessage PostItem(Item myItem)
{
	HttpResponseMessage response;
	string uri;
	if (!ModelState.IsValid)
	{
		List<string> errors = new List<string>();
		foreach (System.Web.Http.ModelBinding.ModelState v in ModelState.Values)
		{
			if (v.Errors.Count > 0)
			{
				errors.Add(v.Errors[0].ErrorMessage);
			}
		}
		response = Request.CreateResponse(HttpStatusCode.BadRequest, errors.ToArray());
		return response;
	}
	items.Add(myItem);
	response = Request.CreateResponse(HttpStatusCode.Created, myItem);
	uri = Url.Link("DefaultApi", new { id = myItem.term });
	response.Headers.Location = new Uri(uri);
	return response;
}

Rispetto alla versione originale abbiamo fatto precedere al salvataggio vero e proprio un if che ha il compito di verificare la validità del ModelState corrente. Se questo non è valido eseguiamo un ciclo alla ricerca degli errori di validazione e mettiamo i messaggi di errore trovati in un array di stringhe. Quindi confezioniamo una risposta da inviare al client con codice HTTP 400 (HttpStatusCode.BadRequest) contenente l'elenco dei messaggi d'errore e gliela inviamo, senza proseguire con il salvataggio.

Sarà compito del client gestire opportunamente l'errore di validazione. Ad esempio, nel caso di client JavaScript gli eventuali errori di validazione potrebbero essere gestiti nel seguente modo:

$.post(	"api/items",
	item,
	function() {
		$("#divMessage").html("Salvataggio effettuato correttamente!");
	})
	.error(function(xhr) {
			var errors = JSON.parse(xhr.responseText);
			var strErrors = "";
			$.each(errors, function(index, err) {
				strErrors += "* " + err + "<br/>";
			});
			$("#divMessage").html(strErrors);
		});

Se non ci sono errori viene visualizzato in un apposito div il messaggio "Salvataggio effettuato correttamente!". Se si sono verificati degli errori, viene creato l'oggetto JSON relativo alla risposta della nostra API e viene generata una stringa dei messaggi di errore da visualizzare come HTML nel solito div.

Il codice fin qui presentato è perfettamente funzionante, ma se analizziamo meglio il brano di codice che abbiamo aggiunto al metodo PostItem() possiamo vedere che è del tutto generico. Non viene fatto alcun riferimento al tipo di risorsa gestita dal controller.

Possiamo pertanto migliorare la manutenibilità del sistema implementando la validazione come un Action filter di validazione, cioè di un filtro della richiesta HTTP da eseguire prima di coinvolgere la risorsa della nostra API. Creiamo quindi nel nostro progetto una cartella Filters ed inseriamo al suo interno la classe ValidationActionFilter. La classe verrà implementata come derivata da ActionFilterAttribute e conterrà essenzialmente il codice che avevamo inserito all'inizio del metodo PostItem():

using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace Glossario.Filters
{
    public class ValidationActionFilter:ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
		var myModelState = actionContext.ModelState;
		if (!myModelState.IsValid)
		{
			List<string> errors = new List<string>();
			foreach (System.Web.Http.ModelBinding.ModelState v in myModelState.Values)
			{
				if (v.Errors.Count > 0)
				{
					errors.Add(v.Errors[0].ErrorMessage);
				}
			}
			actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, errors.ToArray());
	}
        }
    }
}

Le uniche differenze rispetto al codice originario riguardano l'accesso al ModelState e la creazione della risposta HTTP, entrambe le operazioni mediate dal contesto dell'azione corrente (HttpActionContext).

Una volta implementato l'Action filter occorre registrarlo in Global.asax in corrispondenza del metodo Application_Start():

GlobalConfiguration.Configuration.Filters.Add(new Glossario.Filters.ValidationActionFilter());

A questo punto avremo la possibilità di sfruttare gli attributi di validazione in tutto il nostro progetto senza necessità di intervenire sui singoli metodi del controller. Il vantaggio rispetto alla prima soluzione è evidente: abbiamo la possibilità di convalidare il nostro modello dei dati con un approccio dichiarativo, senza modificare i metodi del controller.

Tutto il codice è disponibile in allegato.

Ti consigliamo anche