ASP.NET MVC: convalida personalizzata da DataAnnotation


110

Ho un modello con 4 proprietà che sono di tipo stringa. So che puoi convalidare la lunghezza di una singola proprietà utilizzando l'annotazione StringLength. Tuttavia voglio convalidare la lunghezza delle 4 proprietà combinate.

Qual è il modo MVC per farlo con l'annotazione dei dati?

Lo chiedo perché sono nuovo in MVC e voglio farlo nel modo corretto prima di creare la mia soluzione.


2
Hai esaminato Fluent Validation? Gestisce scenari complessi molto meglio delle annotazioni dei dati
levelnis

Dai un'occhiata alle soluzioni altamente consigliate .... dotnetcurry.com/ShowArticle.aspx?ID=776
Niks

Grazie per aver risposto. Controllerò Fluent Validation, non ne ho mai sentito parlare. E Niks, Darin fondamentalmente ha scritto quello che ha spiegato l'articolo al link che hai pubblicato. Quindi, grazie ... Fantastica roba!
Danny van der Kraan

Risposte:


177

Puoi scrivere un attributo di convalida personalizzato:

public class CombinedMinLengthAttribute: ValidationAttribute
{
    public CombinedMinLengthAttribute(int minLength, params string[] propertyNames)
    {
        this.PropertyNames = propertyNames;
        this.MinLength = minLength;
    }

    public string[] PropertyNames { get; private set; }
    public int MinLength { get; private set; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var properties = this.PropertyNames.Select(validationContext.ObjectType.GetProperty);
        var values = properties.Select(p => p.GetValue(validationContext.ObjectInstance, null)).OfType<string>();
        var totalLength = values.Sum(x => x.Length) + Convert.ToString(value).Length;
        if (totalLength < this.MinLength)
        {
            return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
        }
        return null;
    }
}

e quindi potresti avere un modello di visualizzazione e decorare una delle sue proprietà con esso:

public class MyViewModel
{
    [CombinedMinLength(20, "Bar", "Baz", ErrorMessage = "The combined minimum length of the Foo, Bar and Baz properties should be longer than 20")]
    public string Foo { get; set; }
    public string Bar { get; set; }
    public string Baz { get; set; }
}

4
Grazie per aver risposto, ho accettato la tua risposta. Mi sento un po 'imbarazzato in realtà. Hai scritto l'intera soluzione! Hehe. Doveva solo cambiare la funzione IsValid per controllare la lunghezza massima. Quindi questa è la soluzione MVC accettata per questi tipi di problemi?
Danny van der Kraan

7
@DannyvanderKraan, sì, questo è il modo accettato. Ovviamente questo fa schifo così tanto che non lo uso mai e uso FluentValidation.NET invece per eseguire la convalida.
Darin Dimitrov

11
Qui: fluentvalidation.codeplex.com . Si potrebbe avere appena scritto un semplice validatore per il modello di vista che potrebbe essere guardato come questo (una sola riga di codice): this.RuleFor(x => x.Foo).Must((x, foo) => x.Foo.Length + x.Bar.Length + x.Baz.Length < 20).WithMessage("The combined minimum length of the Foo, Bar and Baz properties should be longer than 20");. Ora guarda il codice nella mia risposta che devi scrivere con le annotazioni dei dati e dimmi quale preferisci. Il modello di convalida dichiarativa è molto povero rispetto a un modello imperativo.
Darin Dimitrov

1
È un po 'tardi, ma qualcuno sa se c'è un'impostazione diversa che devi "attivare" per consentire le annotazioni dei dati personalizzate? So dell'aggiunta di uno spazio dei nomi per js non invadenti sul file web.config, ma da qualche altra parte?
Jose

1
Ho cercato per questo tutta la mattina! L'ho implementato e sfortunatamente quando IsValidviene chiamato validationContextè nullo. Qualche idea su cosa ho sbagliato? :-(
Grimm The Opiner

95

Modello auto convalidato

Il tuo modello dovrebbe implementare un'interfaccia IValidatableObject. Inserisci il tuo codice di convalida nel Validatemetodo:

public class MyModel : IValidatableObject
{
    public string Title { get; set; }
    public string Description { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Title == null)
            yield return new ValidationResult("*", new [] { nameof(Title) });

        if (Description == null)
            yield return new ValidationResult("*", new [] { nameof(Description) });
    }
}

Nota: questa è una convalida lato server . Non funziona sul lato client. La convalida verrà eseguita solo dopo l'invio del modulo.


Grazie per aver risposto Andrei. Anche se la tua soluzione funzionerebbe, scelgo quella di Darin perché è più riutilizzabile.
Danny van der Kraan

6
yield return new ValidationResult ("Il titolo è obbligatorio.", "Titolo"); aggiungerebbe il nome della proprietà, utile per raggruppare gli errori di convalida da visualizzare se necessario.
Mike Kingscott

5
Notare che questo metodo di convalida viene chiamato solo dopo che tutti gli attributi di convalida hanno superato la convalida.
Pedro

3
Questo ha funzionato bene per me poiché la mia convalida era molto specifica. Aggiungere un attributo personalizzato sarebbe stato eccessivo per me poiché la convalida non sarebbe stata riutilizzata.
Steve S,

Questo è quello che cerco. Grazie!
Amol Jadhav

27

ExpressiveAnnotations ti offre questa possibilità:

[Required]
[AssertThat("Length(FieldA) + Length(FieldB) + Length(FieldC) + Length(FieldD) > 50")]
public string FieldA { get; set; }

È brillante! le mie preghiere sono state esaudite :)
Korayem

Ho appena trovato questa risposta e mi ha fatto risparmiare un sacco di tempo. Le annotazioni espressive sono brillanti!
Brad

10

Per migliorare la risposta di Darin, può essere un po 'più breve:

public class UniqueFileName : ValidationAttribute
{
    private readonly NewsService _newsService = new NewsService();

    public override bool IsValid(object value)
    {
        if (value == null) { return false; }

        var file = (HttpPostedFile) value;

        return _newsService.IsFileNameUnique(file.FileName);
    }
}

Modello:

[UniqueFileName(ErrorMessage = "This file name is not unique.")]

Tieni presente che è richiesto un messaggio di errore, altrimenti l'errore sarà vuoto.


8

Sfondo:

Le convalide del modello sono necessarie per garantire che i dati ricevuti che riceviamo siano validi e corretti in modo da poter eseguire l'ulteriore elaborazione con questi dati. Possiamo convalidare un modello in un metodo di azione. Gli attributi di convalida incorporati sono Compare, Range, RegularExpression, Required, StringLength. Tuttavia, potremmo avere scenari in cui abbiamo richiesto attributi di convalida diversi da quelli incorporati.

Attributi di convalida personalizzati

public class EmployeeModel 
{
    [Required]
    [UniqueEmailAddress]
    public string EmailAddress {get;set;}
    public string FirstName {get;set;}
    public string LastName {get;set;}
    public int OrganizationId {get;set;}
}

Per creare un attributo di convalida personalizzato, dovrai derivare questa classe da ValidationAttribute.

public class UniqueEmailAddress : ValidationAttribute
{
    private IEmployeeRepository _employeeRepository;
    [Inject]
    public IEmployeeRepository EmployeeRepository
    {
        get { return _employeeRepository; }
        set
        {
            _employeeRepository = value;
        }
    }
    protected override ValidationResult IsValid(object value,
                        ValidationContext validationContext)
    {
        var model = (EmployeeModel)validationContext.ObjectInstance;
        if(model.Field1 == null){
            return new ValidationResult("Field1 is null");
        }
        if(model.Field2 == null){
            return new ValidationResult("Field2 is null");
        }
        if(model.Field3 == null){
            return new ValidationResult("Field3 is null");
        }
        return ValidationResult.Success;
    }
}

Spero che questo ti aiuti. Saluti !

Riferimenti


1

Un po 'tardi per rispondere, ma per chi sta cercando. Puoi farlo facilmente usando una proprietà extra con l'annotazione dei dati:

public string foo { get; set; }
public string bar { get; set; }

[MinLength(20, ErrorMessage = "too short")]
public string foobar 
{ 
    get
    {
        return foo + bar;
    }
}

Questo è tutto ciò che è davvero. Se vuoi davvero visualizzare in un posto specifico anche l'errore di convalida, puoi aggiungerlo nella tua vista:

@Html.ValidationMessage("foobar", "your combined text is too short")

farlo nella vista può tornare utile se vuoi fare la localizzazione.

Spero che questo ti aiuti!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.