Sostituire le eccezioni di lancio con la notifica nelle convalide

2

Martin Fowler raccomanda sostituire le eccezioni con le notifiche quando si tratta di convalide. In sostanza, anziché generare un'eccezione, si aggiungono messaggi di errore a un oggetto Notification che viene restituito. Ciò ti consente di mantenere le eccezioni in circostanze "eccezionali" e di restituire tutte le informazioni di convalida, non solo il primo verificato.

L'idea ha un senso. Tuttavia, come si applica questa idea nei casi in cui un metodo esegue la convalida di un input utente ma restituisce anche un oggetto?

    
posta Joao Pereira 12.12.2014 - 19:01
fonte

1 risposta

4

Aggiungendo alcuni metadati al tuo oggetto, ereditando da una classe astratta che contiene quei dati, scrivendo un oggetto composito o qualcosa di simile.

In ASP.NET MVC 1, questo sarebbe stato fatto allegando un partial class alla tua entità (nel linguaggio C #, le classi che hanno lo stesso nome con il modificatore partial davanti a loro sono semplicemente trattate come una grande singola classe).

Quindi, ad esempio:

public partial class Dinner {

    public bool IsValid {
        get { return (GetRuleViolations().Count() == 0); }
    }

    // Return a list of errors that we can iterate over with 'foreach'
    public IEnumerable<RuleViolation> GetRuleViolations() {

        // Validation rules go here; see below for an example.

        yield break;
    }

    partial void OnValidate(ChangeAction action) {
        if (!IsValid)
            throw new ApplicationException("Rule violations prevent saving");
    }
}

Una violazione delle regole è solo un oggetto immutabile contenente un messaggio di errore e un nome di proprietà:

public class RuleViolation {

    public string ErrorMessage { get; private set; }
    public string PropertyName { get; private set; }

    public RuleViolation(string errorMessage, string propertyName) {
        ErrorMessage = errorMessage;
        PropertyName = propertyName;
    }
}

Quindi verificherai errori come questo:

public IEnumerable<RuleViolation> GetRuleViolations() {

    if (String.IsNullOrEmpty(Title))
        yield return new RuleViolation("Title required","Title");

    if (String.IsNullOrEmpty(Description))
        yield return new RuleViolation("Description required","Description");

    if (String.IsNullOrEmpty(HostedBy))
        yield return new RuleViolation("HostedBy required", "HostedBy");

    if (String.IsNullOrEmpty(Address))
        yield return new RuleViolation("Address required", "Address");

    if (String.IsNullOrEmpty(Country))
        yield return new RuleViolation("Country required", "Country");

    if (String.IsNullOrEmpty(ContactPhone))
        yield return new RuleViolation("Phone# required", "ContactPhone");

    if (!PhoneValidator.IsValidNumber(ContactPhone, Country))
        yield return new RuleViolation("Phone# does not match country", "ContactPhone");

    yield break;
}

Questa disposizione consente di riportare gli errori a una vista in modo che possano essere visualizzati all'utente, in questo modo (Questo è un metodo di controller, accetta un modulo POST da un modulo Web utente):

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {

        // Map form fields to dinner object
        UpdateModel(dinner);

        // Validation occurs here.  Throws exception if validation fails
        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {

        // Add each rule violation to the View, so that errors can be displayed.
        foreach (var issue in dinner.GetRuleViolations()) {
            ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
        }

        // Display form with validation errors.
        return View(dinner);
    }
}

Si noti che l'eccezione è lì solo per attivare il processo di gestione della convalida; non esiste un'eccezione per ogni possibile errore di convalida. Invece, le regole di convalida sono codificate nel metodo GetRuleViolations() e vengono tutte gestite contemporaneamente.

Naturalmente, al giorno d'oggi, il processo è diventato un po 'più raffinato. Nelle versioni più recenti di ASP.NET MVC, gli attributi vengono utilizzati per codificare le regole di convalida. Ma il principio è sempre lo stesso: non si usano eccezioni per codificare le singole regole di convalida. Nota i metadati di convalida che sono codificati negli attributi del codice sottostante (gli attributi sono quelle righe di codice che sono racchiuse tra parentesi):

public class Movie
{
    public int ID { get; set; }

    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; }

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

    [RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
    [Required]
    [StringLength(30)]
    public string Genre { get; set; }

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

    [RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
    [StringLength(5)]
    public string Rating { get; set; }
}
    
risposta data 12.12.2014 - 19:34
fonte

Leggi altre domande sui tag