การตรวจสอบตามเงื่อนไข ASP.NET MVC


129

จะใช้คำอธิบายประกอบข้อมูลเพื่อทำการตรวจสอบความถูกต้องตามเงื่อนไขกับรุ่นได้อย่างไร

ตัวอย่างเช่นสมมติว่าเรามีโมเดลดังต่อไปนี้ (บุคคลและอาวุโส):

public class Person
{
    [Required(ErrorMessage = "*")]
    public string Name
    {
        get;
        set;
    }

    public bool IsSenior
    {
        get;
        set;
    }

    public Senior Senior
    {
        get;
        set;
    }
}

public class Senior
{
    [Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value
    public string Description
    {
        get;
        set;
    }
}

และมุมมองต่อไปนี้:

<%= Html.EditorFor(m => m.Name)%>
<%= Html.ValidationMessageFor(m => m.Name)%>

<%= Html.CheckBoxFor(m => m.IsSenior)%>
<%= Html.ValidationMessageFor(m => m.IsSenior)%>

<%= Html.CheckBoxFor(m => m.Senior.Description)%>
<%= Html.ValidationMessageFor(m => m.Senior.Description)%>

ฉันต้องการเป็นฟิลด์ "คุณสมบัติอาวุโสคำอธิบาย" ตามเงื่อนไขที่เลือกตามการเลือก "IsSenior" ทรัพย์ (จริง -> จำเป็น) วิธีการใช้การตรวจสอบตามเงื่อนไขใน ASP.NET MVC 2 พร้อมคำอธิบายประกอบข้อมูล?


1
ฉันเพิ่งถามคำถามที่คล้ายกัน: stackoverflow.com/questions/2280539/…
ดารินดิมิทรอฟ

ฉันสับสน Seniorวัตถุอยู่เสมออาวุโสดังนั้นทำไมสามารถ IsSenior เป็นเท็จในกรณีที่ คุณไม่เพียงต้องการคุณสมบัติ 'Person.Senior' เป็นโมฆะเมื่อPerson.IsSeniorเป็นเท็จ หรือทำไมไม่ใช้คุณสมบัติดังต่อไปนี้:IsSenior bool IsSenior { get { return this.Senior != null; } }
Steven

Steven: "IsSenior" แปลเป็นช่องทำเครื่องหมายในมุมมอง เมื่อผู้ใช้ตรวจสอบช่องทำเครื่องหมาย "IsSenior" แล้วฟิลด์ "Senior.Description" จะกลายเป็นข้อบังคับ
Peter Stegnar

ดารินดิมิททรอฟ: ค่อนข้างเรียงลำดับ แต่ไม่มากนัก คุณจะเห็นได้อย่างไรว่าคุณจะประสบความสำเร็จได้อย่างไรว่าข้อความแสดงข้อผิดพลาดปรากฏขึ้นในฟิลด์ที่ระบุ หากคุณตรวจสอบที่ระดับวัตถุคุณจะได้รับข้อผิดพลาดที่ระดับวัตถุ ฉันต้องการข้อผิดพลาดในระดับของคุณสมบัติ
Peter Stegnar

คำตอบ:


150

มีวิธีที่ดีกว่ามากในการเพิ่มกฎการตรวจสอบตามเงื่อนไขใน MVC3 ให้โมเดลของคุณสืบทอดIValidatableObjectและใช้Validateเมธอด:

public class Person : IValidatableObject
{
    public string Name { get; set; }
    public bool IsSenior { get; set; }
    public Senior Senior { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
        if (IsSenior && string.IsNullOrEmpty(Senior.Description)) 
            yield return new ValidationResult("Description must be supplied.");
    }
}

อ่านเพิ่มเติมได้ที่แนะนำ ASP.NET MVC 3 (ดูตัวอย่าง 1)


ถ้าคุณสมบัติเป็นประเภท "int" ที่ต้องการค่าถ้ากรอกข้อมูลในฟิลด์นั้นการตรวจสอบจะไม่ทำงาน ..
Jeyhun Rahimov

2
น่าเสียดายที่ Microsoft วางสิ่งนี้ในเลเยอร์ที่ไม่ถูกต้อง - การตรวจสอบเป็นตรรกะทางธุรกิจและส่วนต่อประสานนี้อยู่ใน System.Web DLL ในการใช้สิ่งนี้คุณต้องให้ชั้นธุรกิจของคุณพึ่งพาเทคโนโลยีการนำเสนอ
NightOwl888

7
คุณทำถ้าคุณใช้มัน - ดูตัวอย่างทั้งหมดได้ที่falconwebtech.com/post/ …
viperguynaz

4
falconwebtech.com/post/… - @viperguynaz ไม่สามารถใช้งานได้
Smit Patel

1
@ RayLoveless คุณควรจะโทรModelState.IsValid- ไม่โทรตรวจสอบโดยตรง
viperguynaz

63

ฉันแก้ไขมันด้วยการจัดการพจนานุกรม"ModelState"ซึ่งมีคอนโทรลเลอร์อยู่ พจนานุกรม ModelState ประกอบด้วยสมาชิกทั้งหมดที่ต้องได้รับการตรวจสอบความถูกต้อง

นี่คือทางออก:

หากคุณจำเป็นต้องใช้การตรวจสอบความถูกต้องตามเงื่อนไขโดยยึดตามฟิลด์บางฟิลด์ (เช่นถ้า A = จริงจำเป็นต้องใช้ B) ในขณะที่ยังคงส่งข้อความแสดงข้อผิดพลาดระดับคุณสมบัติ (นี่ไม่เป็นความจริงสำหรับเครื่องมือตรวจสอบความถูกต้องเอง โดยจัดการ "ModelState" เพียงแค่ลบการตรวจสอบที่ไม่ต้องการออก

... ในบางชั้น ...

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

[Required(ErrorMessage = "*")] 
public string DepentedProperty
{
  get;
  set;
}

... เรียนต่อ ...

... ในการดำเนินการควบคุมบางอย่าง ...

if (!PropertyThatRequiredAnotherFieldToBeFilled)
{
   this.ModelState.Remove("DepentedProperty");
}

...

ด้วยวิธีนี้เราสามารถตรวจสอบความถูกต้องตามเงื่อนไขในขณะที่ปล่อยให้ทุกอย่างเหมือนเดิม


UPDATE:

นี่คือการใช้งานครั้งสุดท้ายของฉัน: ฉันได้ใช้อินเทอร์เฟซกับโมเดลและแอ็ตทริบิวต์การดำเนินการที่ตรวจสอบความถูกต้องของโมเดลซึ่งใช้อินเทอร์เฟซดังกล่าว อินเตอร์เฟสกำหนดเมธอด Validate (ModelStateDictionary modelState) แอ็ตทริบิวต์การดำเนินการเพียงเรียกใช้ Validate (modelState) บน IValidatorSomething

ฉันไม่ต้องการที่จะทำให้คำตอบนี้ซับซ้อนดังนั้นฉันจึงไม่ได้กล่าวถึงรายละเอียดการใช้งานขั้นสุดท้าย (ซึ่งในตอนท้ายเรื่องในรหัสการผลิต)


17
ข้อเสียคือส่วนหนึ่งของตรรกะการตรวจสอบของคุณอยู่ในรูปแบบและส่วนอื่น ๆ ในตัวควบคุม
Kristof Claes

แน่นอนว่ามันไม่จำเป็น ฉันแค่แสดงตัวอย่างพื้นฐานที่สุด ฉันใช้สิ่งนี้กับอินเตอร์เฟสบนโมเดลและแอ็ตทริบิวต์ action ที่ตรวจสอบโมเดลซึ่งใช้อินเตอร์เฟสที่กล่าวถึง ส่วนต่อประสานจะตรวจสอบเมธอด Validate (ModelStateDictionary modelState) ดังนั้นในที่สุดคุณจะตรวจสอบความถูกต้องทั้งหมดในโมเดล อย่างไรก็ตามจุดที่ดี
Peter Stegnar

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

2
@Aaron: ฉันดีใจที่คุณชอบโซลูชัน แต่น่าเสียดายที่โซลูชันนี้ไม่สามารถใช้กับการตรวจสอบความถูกต้องของฝั่งไคลเอ็นต์ได้ คุณสามารถช่วยตัวเองด้วยคุณสมบัติ "ระยะไกล" ดังนั้นเพียงโทร Ajax จะถูกปล่อยออกมาเพื่อตรวจสอบมัน
Peter Stegnar

คุณสามารถขยายคำตอบนี้ได้หรือไม่? มันทำให้รู้สึกบางอย่าง แต่ฉันต้องการให้แน่ใจว่าฉันเป็นคริสตัลบนมัน ฉันต้องเผชิญกับสถานการณ์ที่แน่นอนและฉันต้องการแก้ไขมัน
Richard B

36

ฉันมีปัญหาเดียวกันเมื่อวานนี้ แต่ฉันทำมันด้วยวิธีที่สะอาดซึ่งใช้ได้กับทั้งการตรวจสอบฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์

เงื่อนไข: ตามมูลค่าของคุณสมบัติอื่นในโมเดลคุณต้องการสร้างคุณสมบัติอื่นที่ต้องการ นี่คือรหัส

public class RequiredIfAttribute : RequiredAttribute
{
    private String PropertyName { get; set; }
    private Object DesiredValue { get; set; }

    public RequiredIfAttribute(String propertyName, Object desiredvalue)
    {
        PropertyName = propertyName;
        DesiredValue = desiredvalue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        Object instance = context.ObjectInstance;
        Type type = instance.GetType();
        Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
        if (proprtyvalue.ToString() == DesiredValue.ToString())
        {
            ValidationResult result = base.IsValid(value, context);
            return result;
        }
        return ValidationResult.Success;
    }
}

นี่ PropertyName เป็นคุณสมบัติที่คุณต้องการทำให้เงื่อนไขของคุณ DesiredValue เป็นค่าเฉพาะของ PropertyName (คุณสมบัติ) ซึ่งคุณสมบัติอื่นของคุณจะต้องได้รับการตรวจสอบสำหรับความต้องการ

สมมติว่าคุณมีดังต่อไปนี้

public class User
{
    public UserType UserType { get; set; }

    [RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))]
    public string Password
    {
        get;
        set;
    }
}

ในที่สุด แต่ไม่ท้ายสุดลงทะเบียนอะแดปเตอร์สำหรับแอตทริบิวต์ของคุณเพื่อให้สามารถทำการตรวจสอบด้านไคลเอนต์ (ฉันใส่ไว้ใน global.asax, Application_Start)

 DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));

นี่คือจุดเริ่มต้นเดิมmiroprocessordev.blogspot.com/2012/08/…
Dan Hunex

มีวิธีแก้ปัญหาที่เท่าเทียมกันใน asp.net mvc2 หรือไม่? ValidationResult คลาส ValidationContext ไม่สามารถใช้งานได้ใน asp.net mvc2 (. net framework 3.5)
User_MVC

2
ใช้งานได้เฉพาะฝั่งเซิร์ฟเวอร์เป็นสถานะบล็อกที่เชื่อมโยง
Pakman

2
ฉันจัดการเพื่อให้การทำงานนี้ในฝั่งไคลเอ็นต์ด้วย MVC5 แต่ในไคลเอนต์มันเริ่มตรวจสอบความถูกต้องไม่ว่า DesiredValue คืออะไร
Geethanga

1
@Dan Hunex: ใน MVC4 ฉันยังไม่สามารถทำงานได้อย่างถูกต้องในฝั่งไคลเอ็นต์และจะทำให้เกิดการตรวจสอบความถูกต้องไม่ว่า DesiredValue จะเป็นอย่างไร กรุณาช่วยใด ๆ
แจ็ค

34

ฉันใช้ก้อนสีแดงที่น่าทึ่งนี้ซึ่งทำหมายเหตุประกอบแบบไดนามิกExpressiveAnnotations

คุณสามารถตรวจสอบตรรกะใด ๆ ที่คุณสามารถฝันถึง:

public string Email { get; set; }
public string Phone { get; set; }
[RequiredIf("Email != null")]
[RequiredIf("Phone != null")]
[AssertThat("AgreeToContact == true")]
public bool? AgreeToContact { get; set; }

3
ไลบรารี ExpressiveAnnotation เป็นคำตอบที่ยืดหยุ่นที่สุดสำหรับคำตอบทั้งหมดที่นี่ ขอบคุณสำหรับการแบ่งปัน!
Sudhanshu Mishra

2
ฉันต่อสู้กับหัวของฉันพยายามหาวิธีแก้ปัญหามาตลอดทั้งวัน ExpressiveAnnotations ดูเหมือนจะเป็นการแก้ไขสำหรับฉัน!
Caverman

ExpressiveAnnotation library น่ากลัวมาก!
Doug Knudsen

1
มีฝ่ายสนับสนุนลูกค้าเช่นกัน!
Nattrass

1
แม้ว่าจะไม่มีการรองรับ. NET Core และดูเหมือนจะไม่เกิดขึ้น
gosr

18

คุณสามารถปิดใช้งานเครื่องมือตรวจสอบความถูกต้องตามเงื่อนไขโดยการลบข้อผิดพลาดจาก ModelState:

ModelState["DependentProperty"].Errors.Clear();


6

ขณะนี้มีเฟรมเวิร์กที่ทำการตรวจสอบความถูกต้องตามเงื่อนไขนี้ (จากการตรวจสอบคำอธิบายประกอบข้อมูลที่มีประโยชน์อื่น ๆ ) นอกกรอบ: http://foolproof.codeplex.com/

ให้ดูที่ตัวตรวจสอบความถูกต้องของ [RequiredIfTrue ("IsSenior")] คุณใส่คุณสมบัตินั้นโดยตรงบนคุณสมบัติที่คุณต้องการตรวจสอบเพื่อให้คุณได้รับพฤติกรรมที่ต้องการของข้อผิดพลาดในการตรวจสอบความถูกเชื่อมโยงกับคุณสมบัติ "อาวุโส"

มันสามารถใช้ได้เป็นแพคเกจ NuGet


3

คุณต้องตรวจสอบที่ระดับบุคคลไม่ใช่ระดับอาวุโสหรือผู้อาวุโสต้องมีการอ้างอิงถึงบุคคลหลัก ดูเหมือนว่าคุณต้องการกลไกการตรวจสอบตนเองที่กำหนดการตรวจสอบความถูกต้องของบุคคลไม่ใช่คุณสมบัติอย่างใดอย่างหนึ่ง ฉันไม่แน่ใจ แต่ฉันไม่คิดว่า DataAnnotations รองรับสิ่งนี้นอกกรอบ สิ่งที่คุณสามารถสร้างของคุณเองAttributeที่เกิดขึ้นจากValidationAttributeสิ่งนั้นสามารถตกแต่งในระดับชั้นเรียนและสร้างตัวตรวจสอบความถูกต้องที่กำหนดเองซึ่งจะช่วยให้ตัวตรวจสอบระดับชั้นเหล่านั้นสามารถทำงานได้

ฉันรู้ว่า Validation Application Block รองรับการตรวจสอบตัวเองนอกกรอบ แต่ VAB มีช่วงการเรียนรู้ที่สูงชัน อย่างไรก็ตามนี่คือตัวอย่างการใช้ VAB:

[HasSelfValidation]
public class Person
{
    public string Name { get; set; }
    public bool IsSenior { get; set; }
    public Senior Senior { get; set; }

    [SelfValidation]
    public void ValidateRange(ValidationResults results)
    {
        if (this.IsSenior && this.Senior != null && 
            string.IsNullOrEmpty(this.Senior.Description))
        {
            results.AddResult(new ValidationResult(
                "A senior description is required", 
                this, "", "", null));
        }
    }
}

"คุณต้องตรวจสอบที่ระดับบุคคลไม่ใช่ระดับอาวุโส" ใช่นี่เป็นตัวเลือก แต่คุณสูญเสียความสามารถที่ข้อผิดพลาดถูกผนวกเข้ากับฟิลด์เฉพาะซึ่งจำเป็นต้องใช้ในวัตถุอาวุโส
Peter Stegnar

3

ฉันมีปัญหาเดียวกันต้องการแก้ไขแอตทริบิวต์ [จำเป็น] - สร้างฟิลด์ที่ต้องอาศัยการร้องขอ http การแก้ปัญหาคล้ายกับคำตอบของ Dan Hunex แต่โซลูชันของเขาทำงานไม่ถูกต้อง (ดูความคิดเห็น) ฉันไม่ได้ใช้การตรวจสอบที่ไม่เป็นการรบกวนเพียงแค่ MicrosoftMvcValidation.js ออกมานอกกรอบ นี่มันคือ ใช้แอตทริบิวต์ที่กำหนดเองของคุณ:

public class RequiredIfAttribute : RequiredAttribute
{

    public RequiredIfAttribute(/*You can put here pararmeters if You need, as seen in other answers of this topic*/)
    {

    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {

    //You can put your logic here   

        return ValidationResult.Success;//I don't need its server-side so it always valid on server but you can do what you need
    }


}

จากนั้นคุณต้องใช้ผู้ให้บริการกำหนดเองของคุณเพื่อใช้เป็นอะแดปเตอร์ใน global.asax ของคุณ

public class RequreIfValidator : DataAnnotationsModelValidator <RequiredIfAttribute>
{

    ControllerContext ccontext;
    public RequreIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
       : base(metadata, context, attribute)
    {
        ccontext = context;// I need only http request
    }

//override it for custom client-side validation 
     public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
     {       
               //here you can customize it as you want
         ModelClientValidationRule rule = new ModelClientValidationRule()
         {
             ErrorMessage = ErrorMessage,
    //and here is what i need on client side - if you want to make field required on client side just make ValidationType "required"    
             ValidationType =(ccontext.HttpContext.Request["extOperation"] == "2") ? "required" : "none";
         };
         return new ModelClientValidationRule[] { rule };
      }
}

และแก้ไข global.asax ของคุณด้วยเส้น

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequreIfValidator));

และนี่คือ

[RequiredIf]
public string NomenclatureId { get; set; }

ข้อได้เปรียบที่สำคัญสำหรับฉันคือฉันไม่ต้องใช้รหัสโปรแกรมตรวจสอบลูกค้าที่กำหนดเองเช่นเดียวกับกรณีของการตรวจสอบที่ไม่สร้างความรำคาญ มันทำงานได้เหมือน [จำเป็น] แต่เฉพาะในกรณีที่คุณต้องการ


ส่วนเกี่ยวกับการขยายDataAnnotationsModelValidatorคือสิ่งที่ฉันต้องการเห็น ขอบคุณ.
twip


0

การใช้งานทั่วไปสำหรับการลบข้อผิดพลาดตามเงื่อนไขจาก Model State:

  1. ทำให้ส่วนแรกมีเงื่อนไขของการดำเนินการควบคุม
  2. ดำเนินการตรรกะเพื่อลบข้อผิดพลาดจาก ModelState
  3. ทำส่วนที่เหลือของตรรกะที่มีอยู่ (โดยทั่วไปการตรวจสอบรุ่นของรัฐแล้วทุกอย่างอื่น)

ตัวอย่าง:

public ActionResult MyAction(MyViewModel vm)
{
    // perform conditional test
    // if true, then remove from ModelState (e.g. ModelState.Remove("MyKey")

    // Do typical model state validation, inside following if:
    //     if (!ModelState.IsValid)

    // Do rest of logic (e.g. fetching, saving

ในตัวอย่างของคุณเก็บทุกอย่างตามที่เป็นอยู่และเพิ่มตรรกะที่แนะนำให้กับ Action ของคอนโทรลเลอร์ของคุณ ฉันสมมติว่า ViewModel ของคุณส่งผ่านไปยังแอ็คชันคอนโทรลเลอร์มีออบเจ็กต์ Person and Senior Person ที่มีข้อมูลบรรจุอยู่ใน UI เหล่านั้น


0

ฉันใช้ MVC 5 แต่คุณสามารถลองสิ่งนี้:

public DateTime JobStart { get; set; }

[AssertThat("StartDate >= JobStart", ErrorMessage = "Time Manager may not begin before job start date")]
[DisplayName("Start Date")]
[Required]
public DateTime? StartDate { get; set; }

ในกรณีของคุณคุณอาจพูดว่า "IsSenior == true" จากนั้นคุณเพียงแค่ต้องตรวจสอบความถูกต้องของการโพสต์ของคุณ

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