ASP.NET MVC: Validasi Kustom oleh DataAnnotation

110

Saya memiliki Model dengan 4 properti yang bertipe string. Saya tahu Anda dapat memvalidasi panjang satu properti dengan menggunakan anotasi StringLength. Namun saya ingin memvalidasi panjang dari 4 properti yang digabungkan.

Apa cara MVC untuk melakukan ini dengan anotasi data?

Saya menanyakan ini karena saya baru mengenal MVC dan ingin melakukannya dengan cara yang benar sebelum membuat solusi sendiri.

Danny van der Kraan
sumber
2
Sudahkah Anda melihat Validasi Lancar? Ini menangani skenario kompleks jauh lebih baik daripada Anotasi Data
levelnis
Lihatlah solusi yang sangat direkomendasikan .... dotnetcurry.com/ShowArticle.aspx?ID=776
Niks
Terimakasih telah menjawab. Saya akan memeriksa Validasi Lancar, belum pernah mendengarnya. Dan Niks, Darin pada dasarnya menulis apa yang dijelaskan artikel di tautan yang Anda posting. Jadi, terima kasih ... Hal yang luar biasa!
Danny van der Kraan

Jawaban:

177

Anda dapat menulis atribut validasi khusus:

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;
    }
}

lalu Anda mungkin memiliki model tampilan dan menghias salah satu propertinya dengannya:

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; }
}
Darin Dimitrov
sumber
4
Terima kasih telah menjawab, saya menerima jawaban Anda. Merasa agak malu sebenarnya. Anda menulis seluruh solusi! Hehe. Hanya perlu mengubah fungsi IsValid untuk memeriksa panjang maksimal. Jadi, apakah ini solusi MVC yang diterima untuk jenis masalah ini?
Danny van der Kraan
7
@DannyvanderKraan, ya, ini adalah cara yang diterima. Tentu saja ini sangat menyebalkan sehingga saya tidak pernah menggunakannya dan menggunakan FluentValidation.NET sebagai gantinya untuk melakukan validasi.
Darin Dimitrov
11
Di sini: fluentvalidation.codeplex.com . Anda bisa saja menulis sebuah validator sederhana untuk tampilan model yang mungkin tampak seperti ini (satu baris kode): 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");. Sekarang lihat kode dalam jawaban saya yang perlu Anda tulis dengan anotasi data dan beri tahu saya mana yang Anda sukai. Model validasi deklaratif sangat buruk dibandingkan dengan model imperatif.
Darin Dimitrov
1
Ini agak terlambat, tetapi apakah ada yang tahu jika ada setelan berbeda yang harus Anda "aktifkan" untuk mengizinkan anotasi data ubahsuaian? Saya tahu tentang menambahkan namespace untuk js yang tidak mengganggu di file web.config, tetapi di tempat lain?
Jose
1
Saya sudah mencari ini sepanjang pagi! Saya telah mengimplementasikannya, dan sayangnya ketika IsValiddipanggil validationContextadalah null. Tahu apa yang saya lakukan salah? :-(
Grimm The Opiner
95

Model yang divalidasi sendiri

Model Anda harus mengimplementasikan sebuah antarmuka IValidatableObject. Masukkan kode validasi Anda ke dalam Validatemetode:

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) });
    }
}

Harap perhatikan: ini adalah validasi sisi server . Itu tidak bekerja di sisi klien. Validasi Anda akan dilakukan hanya setelah pengiriman formulir.

Andrei
sumber
Terima kasih telah menjawab Andrei. Sementara solusi Anda juga akan berhasil, saya memilih Darin's karena lebih dapat digunakan kembali.
Danny van der Kraan
6
menghasilkan pengembalian baru ValidationResult ("Judul wajib diisi.", "Judul"); akan menambahkan nama properti, berguna dalam mengelompokkan kesalahan validasi untuk ditampilkan jika perlu.
Mike Kingscott
5
Perhatikan bahwa metode validasi ini hanya dipanggil setelah semua atribut validasi lulus validasi.
Pedro
3
Ini bekerja dengan baik untuk saya karena validasi saya sangat spesifik. Menambahkan atribut khusus akan berlebihan bagi saya karena validasi tidak akan digunakan kembali.
Steve S
Inilah yang saya cari. Terima kasih!
Amol Jadhav
27

ExpressiveAnnotations memberi Anda kemungkinan seperti itu:

[Required]
[AssertThat("Length(FieldA) + Length(FieldB) + Length(FieldC) + Length(FieldD) > 50")]
public string FieldA { get; set; }
jwaliszko
sumber
Ini luar biasa! doa saya dijawab :)
Korayem
Baru saja menemukan jawaban ini dan ini hanya menghemat banyak waktu. ExpressiveAnnotations sangat brilian!
Brad
10

Untuk memperbaiki jawaban Darwin, ini bisa sedikit lebih singkat:

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);
    }
}

Model:

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

Perhatikan bahwa pesan kesalahan diperlukan, jika tidak kesalahan akan kosong.

Jamie
sumber
8

Latar Belakang:

Validasi model diperlukan untuk memastikan bahwa data yang diterima yang kami terima valid dan benar sehingga kami dapat melakukan pemrosesan lebih lanjut dengan data ini. Kami dapat memvalidasi model dalam metode tindakan. Atribut validasi bawaan adalah Compare, Range, RegularExpression, Required, StringLength. Namun kami mungkin memiliki skenario di mana kami memerlukan atribut validasi selain yang sudah ada di dalamnya.

Atribut Validasi Kustom

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;}
}

Untuk membuat atribut validasi khusus, Anda harus mendapatkan kelas ini dari 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;
    }
}

Semoga ini membantu. Bersulang !

Referensi

Yasser Shaikh
sumber
1

Agak terlambat untuk menjawab, tapi untuk siapa yang mencari. Anda dapat dengan mudah melakukan ini dengan menggunakan properti tambahan dengan anotasi data:

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

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

Itu semua terlalu itu. Jika Anda benar-benar ingin menampilkan kesalahan validasi di tempat tertentu juga, Anda dapat menambahkan ini dalam tampilan Anda:

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

melakukan ini dalam tampilan bisa berguna jika Anda ingin melakukan pelokalan.

Semoga ini membantu!

Leo Muller
sumber