使用具有相似模型和相同视图的数据注释来进行不同的验证

发布于 2024-12-17 00:16:02 字数 300 浏览 3 评论 0原文

我有两个从同一接口派生的独立类,但分配了不同的验证/数据注释。要求是需要收集相同的数据,但在一个屏幕上不需要任何内容​​(保存屏幕),但在另一个屏幕上有一些必填字段(提交/完成屏幕)。 我制作了一个 PartialView,用于两个单独的视图,一个用于保存,一个用于最终提交。

我尝试使用父接口作为视图的模型,但是我的验证器不会触发(正如我所期望的,我猜测因为接口本身没有任何注释,所以不会触发任何东西。)有没有办法让页面根据我使用的页面而不是界面动态选择一个类或另一个类?

顺便说一句,这是通过 Razor 在 ASP.net MVC 3 中完成的。

I have two separate classes derived from the same interface, but have different validation/data annotations assigned. The requirement is that the same data needs to be collected, but on one screen nothing is required (a save screen), but on the other there are some required fields (a submit/finalize screen.)
I've made a PartialView that is to be used in two separate View, one for save, one for final submit.

I've tried using the parent Interface as the View's model, however my validators don't fire (as I expect, I'm guessing that because the Interface itself doesn't have any annotations, nothing will fire.) Is there a way to have the page dynamically choose one class or the other depending on which page I'm using instead of the Interface?

As a side-note, this is being done in ASP.net MVC 3 with Razor.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦一生花开无言 2024-12-24 00:16:02

通过一堂课和一点横向思考,您就可以实现您想要的目标。

首先,创建您的类,并内置验证。接下来,创建一个继承自 DataAnnotationsModelValidatorProvider 的自定义 ModelValidatorProvider,如下所示:

public class MyMetadataValidatorProvider : DataAnnotationsModelValidatorProvider
{
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    {
        var vals = base.GetValidators(metadata, context, attributes);
        // check to see if any keys have been inserted
        if (context.Controller.ViewData.Keys.Count > 0)
        {
            // check if we have a key named "NoValidate" with a value of true
            // do not return the validtors if we do
            if ((bool)context.Controller.ViewData.FirstOrDefault(k => k.Key == "NoValidate").Value) 
            {
                // we do not want to return our validators, return an empty list
                return new List<ModelValidator>();
            }
        }
        else
        {
            // check if the form has a key named "NoValidate" with a value of true
            // do not return the validtors if we do
            if (context.HttpContext.Request.Form["NoValidate"].ToLowerInvariant() == "true") 
            {
                // we do not want to return our validators, return an empty list
                return new List<ModelValidator>();
            }
        }

        // we want to return our validators
        return vals;
    }
}

接下来,在 Global 的 Application_Start 中注册自定义 ModelValidatorProvider。 asax.cs,如下所示:

ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new MyMetadataValidatorProvider());

然后,将以下内容添加到您的视图中(这将控制在发布表单时是否返回验证器):

@Html.Hidden("NoValidate", ViewData.FirstOrDefault(k => k.Key == "NoValidate").Value)

最后,添加如下操作:

public ActionResult Index()
{
    var model = new MyModel();
    // this will set validation to appear
    ViewData.Add("NoValidate", false);
    // this will suppress validation 
    ViewData.Add("NoValidate", true);
    return View(model);
}

[HttpPost]
public ActionResult Index(MyModel model)
{
    // we DO want validation, so let's test for it in addition
    // to testing if the ModelState is valid
    if (Request.Form["NoValidate"].ToLowerInvariant() != "true" && ModelState.IsValid)
    {
        ModelState.Clear();
        var newmodel = new MyModel();
        ViewData.Add("NoValidate", true);
        return View(newmodel);
    }

    ViewData.Add("NoValidate", false);
    return View(model);
}

请注意,您可以控制是否显示验证在 GET 操作中,根据需要在 ViewData 中设置 NoValidate 键。在 POST 上,验证由 NoValidate 的表单值控制。

重要提示:在需要验证的操作中,您需要添加一个测试来确认表单没有键 NoValidate,或其值不是 True,在为了强制用户无法逃避验证。

更新

起初,我只有在某些条件成立时才会出现验证。我认为这是一个坏主意,所以现在只有在条件成立时才会抑制验证。

You can achieve what you want with one class, and a little lateral thinking.

First, create your class, with the validation baked in. Next, create a custom ModelValidatorProvider inheriting from DataAnnotationsModelValidatorProvider, like so:

public class MyMetadataValidatorProvider : DataAnnotationsModelValidatorProvider
{
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    {
        var vals = base.GetValidators(metadata, context, attributes);
        // check to see if any keys have been inserted
        if (context.Controller.ViewData.Keys.Count > 0)
        {
            // check if we have a key named "NoValidate" with a value of true
            // do not return the validtors if we do
            if ((bool)context.Controller.ViewData.FirstOrDefault(k => k.Key == "NoValidate").Value) 
            {
                // we do not want to return our validators, return an empty list
                return new List<ModelValidator>();
            }
        }
        else
        {
            // check if the form has a key named "NoValidate" with a value of true
            // do not return the validtors if we do
            if (context.HttpContext.Request.Form["NoValidate"].ToLowerInvariant() == "true") 
            {
                // we do not want to return our validators, return an empty list
                return new List<ModelValidator>();
            }
        }

        // we want to return our validators
        return vals;
    }
}

Next, register the custom ModelValidatorProvider in Application_Start in Global.asax.cs, like so:

ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new MyMetadataValidatorProvider());

Then, add the following to your view (this will govern whether the validators are returned when the form is POSTed):

@Html.Hidden("NoValidate", ViewData.FirstOrDefault(k => k.Key == "NoValidate").Value)

Finally, add actions like the following:

public ActionResult Index()
{
    var model = new MyModel();
    // this will set validation to appear
    ViewData.Add("NoValidate", false);
    // this will suppress validation 
    ViewData.Add("NoValidate", true);
    return View(model);
}

[HttpPost]
public ActionResult Index(MyModel model)
{
    // we DO want validation, so let's test for it in addition
    // to testing if the ModelState is valid
    if (Request.Form["NoValidate"].ToLowerInvariant() != "true" && ModelState.IsValid)
    {
        ModelState.Clear();
        var newmodel = new MyModel();
        ViewData.Add("NoValidate", true);
        return View(newmodel);
    }

    ViewData.Add("NoValidate", false);
    return View(model);
}

Note that you can control whether the validation appears in your GET action by setting the NoValidate key in ViewData as you want. On the POST, the validation is governed by the form value for NoValidate.

IMPORTANT NOTE: In your action which requires validation, you need to add a test to confirm that the Form does not have the key NoValidate, or its value is not True, in order to enforce that a user cannot avoid the validation.

UPDATE

At first, I had validation only appearing when certain conditions were true. Idecided this was a BAD IDEA, so now validation will only be suppressed if the conditions are true.

过度放纵 2024-12-24 00:16:02

每个视图都应该强类型化为单独的视图模型。然后,每个视图模型都具有验证逻辑(注释)或继承自具有所需验证的基础。

任何无法继承的逻辑都只需在 ViewModel 本身上设置即可。如果它是一个小模型,我会考虑仅复制/粘贴和两个具有自己的属性集的单独视图模型。

您可以使用 AutoMapper 轻松地在实现接口的具体对象和 ViewModel 之间进行映射。

Each view should be strongly typed to a separate view model. Each viewmodel then has the validation logic on it (annotations) or inherits from a base that has the required validation on it.

Any logic that cannot be inherited is simply set on your ViewModel itself. If its a small moderl I would consider just copy/paste and two separate viewmodels with their own set of attributes.

You can use AutoMapper to easily map between some concrete object that implements your interface and your ViewModels.

温柔少女心 2024-12-24 00:16:02

你能用一堂课吗?您可以创建一个过滤器来管理操作的验证错误。在您的情况下,您可以向“保存”操作添加一个属性并忽略所需的错误,但将为提交/完成操作运行验证。此示例将丢弃所有错误。

public class DontValidateEmailAttribute : ActionFilterAttribute {

  public override void OnActionExecuting(ActionExecutingContext filterContext) {
    var modelState = filterContext.Controller.ViewData.ModelState; 
    var incomingValues = filterContext.Controller.ValueProvider;

    foreach (var key in modelState.Keys) 
      modelState[key].Errors.Clear();

  }
}

我从 Steve Sanderson 的 Pro ASP NET MVC 3 学到了这项技术。他使用该技术来验证具有必填字段的模型,但数据输入是一个多步骤向导。如果表单发布中未返回该值,他会删除该属性的错误。

Could you use one class? You can create a filter that allows you to manage the validation errors for an action. In your case you can add an attribute to the Save action and ignore the required errors, but the validations will run for the submit/finalize action. This example will discard all the errors.

public class DontValidateEmailAttribute : ActionFilterAttribute {

  public override void OnActionExecuting(ActionExecutingContext filterContext) {
    var modelState = filterContext.Controller.ViewData.ModelState; 
    var incomingValues = filterContext.Controller.ValueProvider;

    foreach (var key in modelState.Keys) 
      modelState[key].Errors.Clear();

  }
}

I learnt this technique from Steve Sanderson's Pro ASP NET MVC 3. He uses the technique to validate a model that has required fields but the data entry is a multistep wizard. If the value has not been returned in the form post, he removes the errors for that property.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文