Xác thực có điều kiện ASP.NET MVC


129

Làm thế nào để sử dụng chú thích dữ liệu để thực hiện xác nhận có điều kiện trên mô hình?

Ví dụ: giả sử chúng ta có mô hình sau (Người và Người cao tuổi):

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

Và quan điểm sau:

<%= 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)%>

Tôi muốn trở thành trường bắt buộc có điều kiện thuộc tính "Senior.Des mô tả" dựa trên việc lựa chọn tính phù hợp "IsSenior" (đúng -> bắt buộc). Làm thế nào để thực hiện xác nhận có điều kiện trong ASP.NET MVC 2 với chú thích dữ liệu?


1
Gần đây tôi đã hỏi câu hỏi tương tự: stackoverflow.com/questions/2280539/
Kẻ

Tôi bối rối. Một Seniorđối tượng luôn là cấp cao, vậy tại sao IsSenior có thể sai trong trường hợp đó. Đừng chỉ cần thuộc tính 'Person.Senior' là null khi Person.IsSeniorsai. Hoặc tại sao không thực hiện các IsSeniortài sản như sau : bool IsSenior { get { return this.Senior != null; } }.
Steven

Steven: "IsSenior" dịch sang trường hộp kiểm trong dạng xem. Khi người dùng kiểm tra hộp kiểm "IsSenior" thì Trường "Senior.Des mô tả" trở thành bắt buộc.
Peter Stegnar

Darin Dimitrov: Cũng khá, nhưng không hoàn toàn. Bạn thấy đấy, làm thế nào bạn đạt được rằng mesage lỗi là phù hợp với lĩnh vực cụ thể? Nếu bạn xác nhận ở cấp đối tượng, bạn sẽ gặp lỗi ở cấp đối tượng. Tôi cần lỗi về cấp độ tài sản.
Peter Stegnar

Câu trả lời:


150

Có một cách tốt hơn nhiều để thêm các quy tắc xác thực có điều kiện trong MVC3; mô hình của bạn kế thừa IValidatableObjectvà thực hiện Validatephương thức:

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

Đọc thêm tại Giới thiệu ASP.NET MVC 3 (Xem trước 1) .


nếu thuộc tính là loại "int", yêu cầu giá trị, nếu điền vào trường đó, Xác thực không hoạt động ..
Jeyhun Rahimov

2
Thật không may, Microsoft đã đặt điều này vào lớp sai - xác thực là logic nghiệp vụ và giao diện này nằm trong System.Web DLL. Để sử dụng điều này, bạn phải cung cấp cho lớp doanh nghiệp của mình một sự phụ thuộc vào công nghệ trình bày.
NightOwl888

7
bạn làm nếu bạn thực hiện nó - xem ví dụ đầy đủ tại falconwebtech.com/post/ từ
viperguynaz

4
falconwebtech.com/post/ từ - @viperguynaz điều này không hoạt động
Smit Patel

1
@RayLovless bạn nên gọi ModelState.IsValid- không gọi trực tiếp Xác thực
viperguynaz

63

Tôi đã giải quyết điều này bằng cách xử lý từ điển "ModelState" , được chứa bởi bộ điều khiển. Từ điển ModelState bao gồm tất cả các thành viên phải được xác nhận.

Đây là giải pháp:

Nếu bạn cần triển khai xác thực có điều kiện dựa trên một số trường (ví dụ: nếu A = true, thì B là bắt buộc), trong khi duy trì thông báo lỗi thuộc tính (điều này không đúng với trình xác thực tùy chỉnh ở cấp đối tượng), bạn có thể đạt được điều này bằng cách xử lý "ModelState", bằng cách xóa các xác nhận không mong muốn khỏi nó.

... Trong một số lớp ...

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

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

... lớp học tiếp tục ...

... Trong một số hành động của bộ điều khiển ...

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

...

Với điều này, chúng tôi đạt được xác nhận có điều kiện, trong khi để mọi thứ khác như cũ.


CẬP NHẬT:

Đây là lần thực hiện cuối cùng của tôi: Tôi đã sử dụng một giao diện trên mô hình và thuộc tính hành động để xác nhận mô hình thực hiện giao diện đã nói. Giao diện quy định phương thức Xác thực (ModelStateDixi modelState). Thuộc tính trên hành động chỉ gọi Xác thực (modelState) trên IValidatorS Something.

Tôi không muốn làm phức tạp câu trả lời này, vì vậy tôi đã không đề cập đến các chi tiết thực hiện cuối cùng (mà cuối cùng, vấn đề trong mã sản xuất).


17
Nhược điểm là một phần logic xác thực của bạn nằm trong mô hình và phần khác trong (các) bộ điều khiển.
Kristof Claes

Tất nhiên điều này là không cần thiết. Tôi chỉ đưa ra ví dụ cơ bản nhất. Tôi đã thực hiện điều này với giao diện trên mô hình và với thuộc tính hành động xác nhận mô hình thực hiện giao diện được đề cập. Giao diện thuyết phục phương thức Xác thực (ModelStateDixi modelState). Vì vậy, cuối cùng bạn LÀM tất cả xác nhận trong mô hình. Dù sao, điểm tốt.
Peter Stegnar

Tôi thích sự đơn giản của phương pháp này trong thời gian trung bình cho đến khi nhóm MVC xây dựng một cái gì đó tốt hơn. Nhưng giải pháp của bạn có hoạt động với xác nhận phía máy khách được kích hoạt không ??
Aaron

2
@Aaron: Tôi rất vui khi bạn thích giải pháp, nhưng thật không may, giải pháp này không hoạt động với xác thực phía máy khách (vì mọi thuộc tính xác thực đều cần triển khai JavaScript). Bạn có thể tự giúp mình với thuộc tính "Remote", do đó, chỉ cần gọi Ajax sẽ được phát ra để xác thực nó.
Peter Stegnar

Bạn có thể mở rộng về câu trả lời này? Điều này có ý nghĩa gì đó, nhưng tôi muốn chắc chắn rằng tôi đang kết tinh nó. Tôi phải đối mặt với tình huống chính xác này và tôi muốn giải quyết nó.
Richard B

36

Tôi đã có cùng một vấn đề ngày hôm qua nhưng tôi đã làm nó theo một cách rất sạch sẽ, hoạt động cho cả xác thực phía máy khách và phía máy chủ.

Điều kiện: Dựa trên giá trị của tài sản khác trong mô hình, bạn muốn tạo một tài sản khác theo yêu cầu. Đây là mã

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

Ở đây PropertyName là thuộc tính mà bạn muốn thực hiện điều kiện của mình DesiredValue là giá trị cụ thể của PropertyName (thuộc tính) mà thuộc tính khác của bạn phải được xác thực theo yêu cầu

Nói rằng bạn có những điều sau đây

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

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

Cuối cùng nhưng không kém phần quan trọng, hãy đăng ký bộ điều hợp cho thuộc tính của bạn để nó có thể xác thực phía máy khách (tôi đặt nó trong global.asax, Application_Start)

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

Đây là điểm khởi đầu ban đầu miro Processordev.blogspot.com/2012/08/ Khăn
Dan

Có bất kỳ giải pháp tương đương trong asp.net mvc2? Các lớp ValidationResult, ValidationContext không có sẵn trong asp.net mvc2 (.net framework 3.5)
User_MVC

2
Điều này chỉ hoạt động phía máy chủ như các trạng thái blog được liên kết
Pakman

2
Tôi đã quản lý để làm việc này ở phía máy khách với MVC5, nhưng trong máy khách, nó kích hoạt xác nhận cho dù DesiredValue là gì.
Geethanga

1
@Dan Hunex: Trong MVC4, tôi đã không quản lý để hoạt động chính xác ở phía máy khách và nó kích hoạt xác nhận cho dù DesiredValue là gì. Có ai giúp đỡ không?
Jack

34

Tôi đã sử dụng cái nuget tuyệt vời này có chú thích động ExpressiveAnnotations

Bạn có thể xác nhận bất kỳ logic nào bạn có thể mơ ước:

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
Thư viện ExpressiveAnnotation là giải pháp linh hoạt và chung nhất trong tất cả các câu trả lời ở đây. Cám ơn vì đã chia sẻ!
Sudhanshu Mishra

2
Tôi đã đập đầu mình để cố gắng tìm giải pháp cho một ngày vững chắc. ExpressiveAnnotations có vẻ là bản sửa lỗi cho tôi!
Caverman

Thư viện ExpressiveAnnotation là tuyệt vời!
Doug Knudsen

1
Nó có hỗ trợ phía khách hàng quá!
Nattrass

1
Không hỗ trợ cho .NET Core và dường như điều đó sẽ không xảy ra.
gosr

18

Bạn có thể vô hiệu hóa trình xác nhận có điều kiện bằng cách xóa lỗi khỏi ModelState:

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


6

Hiện tại có một khung thực hiện xác nhận có điều kiện này (trong số các xác thực chú thích dữ liệu tiện dụng khác) ngoài hộp: http://foolproof.codeplex.com/

Cụ thể, hãy xem trình xác nhận [required IfTrue ("IsSenior")]. Bạn đặt nó trực tiếp lên tài sản bạn muốn xác thực, để bạn có được hành vi mong muốn của lỗi xác thực được liên kết với thuộc tính "Cao cấp".

Nó có sẵn như là một gói NuGet.


3

Bạn cần xác thực ở cấp độ Người, không phải ở cấp độ Cao cấp hoặc Cấp cao phải có một tham chiếu đến Người mẹ của nó. Dường như với tôi rằng bạn cần một cơ chế tự xác thực xác định xác thực trên Người chứ không phải trên một trong các thuộc tính của nó. Tôi không chắc chắn, nhưng tôi không nghĩ DataAnnotations hỗ trợ điều này. Những gì bạn có thể làm tạo của riêng bạn Attributebắt nguồn từValidationAttribute đó có thể được trang trí ở cấp độ lớp và tiếp theo tạo trình xác nhận tùy chỉnh cũng cho phép các trình xác nhận cấp độ lớp đó chạy.

Tôi biết Khối ứng dụng xác thực hỗ trợ tự xác thực, nhưng VAB có đường cong học tập khá dốc. Tuy nhiên, đây là một ví dụ sử dụng 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));
        }
    }
}

"Bạn cần xác thực ở cấp Người, không phải ở Cấp cao" Có, đây là một tùy chọn, nhưng bạn mất khả năng lỗi được thêm vào trường cụ thể, được yêu cầu trong đối tượng Cao cấp.
Peter Stegnar

3

Tôi có cùng một vấn đề, cần sửa đổi thuộc tính [Bắt buộc] - yêu cầu trường phụ thuộc vào yêu cầu http. Giải pháp tương tự như câu trả lời của Dan Hunex, nhưng giải pháp của anh ta không hoạt động chính xác (xem bình luận). Tôi không sử dụng xác nhận không phô trương, chỉ cần MicrosoftMvcValidation.js ra khỏi hộp. Nó đây rồi Thực hiện thuộc tính tùy chỉnh của bạn:

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
    }


}

Sau đó, bạn cần triển khai nhà cung cấp tùy chỉnh của mình để sử dụng nó làm bộ điều hợp trong global.asax của bạn

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

Và sửa đổi global.asax của bạn bằng một dòng

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

và đây

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

Ưu điểm chính đối với tôi là tôi không phải mã hóa trình xác nhận ứng dụng khách tùy chỉnh như trong trường hợp xác thực không phô trương. nó hoạt động giống như [Bắt buộc], nhưng chỉ trong trường hợp bạn muốn.


Phần về mở rộng DataAnnotationsModelValidatorlà chính xác những gì tôi cần thấy. Cảm ơn bạn.
twip


0

Cách sử dụng điển hình để loại bỏ lỗi có điều kiện khỏi Trạng thái Mô hình:

  1. Làm cho phần đầu tiên có điều kiện của hành động điều khiển
  2. Thực hiện logic để xóa lỗi khỏi ModelState
  3. Thực hiện phần còn lại của logic hiện tại (thường là xác thực Trạng thái mô hình, sau đó mọi thứ khác)

Thí dụ:

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

Trong ví dụ của bạn, hãy giữ mọi thứ nguyên trạng và thêm logic được đề xuất vào Hành động của Bộ điều khiển. Tôi giả sử ViewModel của bạn được chuyển đến hành động của bộ điều khiển có các đối tượng Người và Người cao cấp có dữ liệu được nhập từ chúng từ Giao diện người dùng.


0

Tôi đang sử dụng MVC 5 nhưng bạn có thể thử một cái gì đó như thế này:

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

Trong trường hợp của bạn, bạn sẽ nói một cái gì đó như "IsSenior == true". Sau đó, bạn chỉ cần kiểm tra xác nhận về hành động bài viết của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.