ASP.NET MVC: การตรวจสอบแบบกำหนดเองโดย DataAnnotation


110

ฉันมีโมเดลที่มีคุณสมบัติ 4 อย่างซึ่งเป็นสตริงประเภท ฉันรู้ว่าคุณสามารถตรวจสอบความยาวของคุณสมบัติเดียวได้โดยใช้คำอธิบายประกอบ StringLength อย่างไรก็ตามฉันต้องการตรวจสอบความยาวของคุณสมบัติทั้ง 4 ที่รวมกัน

MVC ทำอย่างไรกับคำอธิบายประกอบข้อมูล

ฉันถามสิ่งนี้เพราะฉันเพิ่งเริ่มใช้ MVC และต้องการทำวิธีที่ถูกต้องก่อนที่จะแก้ไขปัญหาของตัวเอง


2
คุณได้ดู Fluent Validation แล้วหรือยัง? จัดการสถานการณ์ที่ซับซ้อนได้ดีกว่าคำอธิบายประกอบข้อมูลมาก
levelnis

ดูโซลูชันที่แนะนำอย่างยิ่ง .... dotnetcurry.com/ShowArticle.aspx?ID=776
Niks

ขอบคุณสำหรับคำตอบ. ฉันจะตรวจสอบ Fluent Validation ไม่เคยได้ยินมาก่อน และ Niks ดารินก็เขียนบทความในลิงค์ที่คุณโพสต์อธิบายไว้ ขอบคุณครับ ... สุดยอดมาก!
Danny van der Kraan

คำตอบ:


177

คุณสามารถเขียนแอตทริบิวต์การตรวจสอบความถูกต้องที่กำหนดเอง:

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

จากนั้นคุณอาจมีโมเดลมุมมองและตกแต่งคุณสมบัติอย่างใดอย่างหนึ่งด้วย:

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
ขอบคุณสำหรับคำตอบฉันยอมรับคำตอบของคุณ รู้สึกอายเล็กน้อยจริงๆ คุณเขียนวิธีแก้ปัญหาทั้งหมด! ฮิฮิ. ต้องเปลี่ยนฟังก์ชัน IsValid เพื่อตรวจสอบความยาวสูงสุดเท่านั้น นี่เป็นโซลูชัน MVC ที่ยอมรับสำหรับปัญหาประเภทนี้หรือไม่?
Danny van der Kraan

7
@DannyvanderKraan ใช่นี่เป็นวิธีที่ยอมรับ แน่นอนว่าสิ่งนี้แย่มากจนฉันไม่เคยใช้เลยและใช้ FluentValidation.NET แทนเพื่อทำการตรวจสอบความถูกต้อง
Darin Dimitrov

11
ที่นี่: fluentvalidation.codeplex.com คุณอาจจะได้เขียนเพียงตรวจสอบง่ายสำหรับรูปแบบมุมมองที่อาจจะมีการมองเช่นนี้ 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");(บรรทัดเดียวของรหัส): ตอนนี้ดูรหัสในคำตอบของฉันที่คุณต้องเขียนด้วยคำอธิบายประกอบข้อมูลและบอกฉันว่าคุณต้องการอันไหน รูปแบบการตรวจสอบความถูกต้องโดยเปิดเผยนั้นแย่มากเมื่อเทียบกับรูปแบบที่จำเป็น
Darin Dimitrov

1
อาจจะช้าไปหน่อย แต่มีใครทราบบ้างไหมว่ามีการตั้งค่าอื่นที่คุณต้อง "เปิด" เพื่ออนุญาตคำอธิบายประกอบข้อมูลที่กำหนดเอง ฉันรู้เกี่ยวกับการเพิ่มเนมสเปซสำหรับ js ที่ไม่เป็นการรบกวนบนไฟล์ web.config แต่ที่อื่น?
Jose

1
ฉันได้รับการมองหานี้ทุกเช้า! ฉันได้ดำเนินการแล้วและน่าเสียดายที่เมื่อIsValidถูกเรียกว่าvalidationContextเป็นโมฆะ มีความคิดอะไรที่ฉันทำผิด? :-(
Grimm The Opiner

96

โมเดลที่ตรวจสอบด้วยตนเอง

IValidatableObjectโมเดลของคุณควรใช้อินเตอร์เฟซ ใส่รหัสยืนยันของคุณในValidateวิธีการ:

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

โปรดสังเกต: นี่เป็นการตรวจสอบฝั่งเซิร์ฟเวอร์ ไม่ทำงานในฝั่งไคลเอ็นต์ การตรวจสอบความถูกต้องของคุณจะดำเนินการหลังจากส่งแบบฟอร์มเท่านั้น


ขอบคุณที่ตอบ Andrei แม้ว่าโซลูชันของคุณจะใช้ได้ผลเช่นกันฉันเลือกของ Darin เพราะสามารถนำกลับมาใช้ใหม่ได้มากกว่า
Danny van der Kraan

6
ให้ผลตอบแทน ValidationResult ใหม่ ("The title is required.", "Title"); จะเพิ่มชื่อคุณสมบัติซึ่งมีประโยชน์ในการจัดกลุ่มข้อผิดพลาดในการตรวจสอบความถูกต้องเพื่อแสดงหากจำเป็น
Mike Kingscott

5
โปรดทราบว่าวิธีการตรวจสอบนี้จะถูกเรียกใช้หลังจากแอตทริบิวต์การตรวจสอบทั้งหมดผ่านการตรวจสอบความถูกต้องแล้วเท่านั้น
Pedro

3
สิ่งนี้ใช้ได้ดีสำหรับฉันเนื่องจากการตรวจสอบของฉันมีความเฉพาะเจาะจงมาก การเพิ่มแอตทริบิวต์ที่กำหนดเองอาจเกินความจำเป็นสำหรับฉันเนื่องจากการตรวจสอบความถูกต้องจะไม่ถูกนำมาใช้ใหม่
Steve S

นี่คือสิ่งที่ฉันกำลังมองหา ขอบคุณ!
Amol Jadhav

27

ExpressiveAnnotationsช่วยให้คุณมีความเป็นไปได้ดังต่อไปนี้:

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

ยอดเยี่ยมมาก! คำอธิษฐานของฉันได้รับคำตอบ :)
Korayem

เพิ่งพบคำตอบนี้และประหยัดเวลาได้มาก คำอธิบายประกอบที่ยอดเยี่ยม!
Brad

10

เพื่อปรับปรุงคำตอบของ Darin ให้สั้นลงเล็กน้อย:

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

รุ่น:

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

โปรดทราบว่าจำเป็นต้องมีข้อความแสดงข้อผิดพลาดมิฉะนั้นข้อผิดพลาดจะว่างเปล่า


8

พื้นหลัง:

จำเป็นต้องมีการตรวจสอบโมเดลเพื่อให้แน่ใจว่าข้อมูลที่ได้รับที่เราได้รับนั้นถูกต้องและถูกต้องเพื่อให้เราสามารถประมวลผลเพิ่มเติมกับข้อมูลนี้ได้ เราสามารถตรวจสอบโมเดลด้วยวิธีการดำเนินการ แอตทริบิวต์การตรวจสอบความถูกต้องในตัว ได้แก่ Compare, Range, RegularExpression, Required, StringLength อย่างไรก็ตามเราอาจมีสถานการณ์ที่เราต้องการแอตทริบิวต์การตรวจสอบความถูกต้องนอกเหนือจากที่มีอยู่ในตัว

แอตทริบิวต์การตรวจสอบที่กำหนดเอง

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

ในการสร้างแอตทริบิวต์การตรวจสอบความถูกต้องที่กำหนดเองคุณจะต้องได้รับคลาสนี้จาก 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;
    }
}

หวังว่านี่จะช่วยได้ ไชโย!

อ้างอิง


1

ตอบช้าไปหน่อย แต่สำหรับใครที่กำลังค้นหา คุณสามารถทำได้อย่างง่ายดายโดยใช้คุณสมบัติพิเศษพร้อมคำอธิบายประกอบข้อมูล:

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

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

นั่นคือทั้งหมดที่มันเกินไปจริงๆ หากคุณต้องการแสดงข้อผิดพลาดในการตรวจสอบความถูกต้องในสถานที่เฉพาะเจาะจงคุณสามารถเพิ่มสิ่งนี้ในมุมมองของคุณ:

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

การทำเช่นนี้ในมุมมองอาจมีประโยชน์หากคุณต้องการแปลเป็นภาษาท้องถิ่น

หวังว่านี่จะช่วยได้!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.