且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

动态启用/禁用MVC服务器端验证

更新时间:2023-11-27 22:30:40

溢出和Bilal,感谢您回答我的问题.

Overflow and Bilal, Thanks for answering my question.

@Bilal:我对保存"和提交"使用相同的模型,并且不希望模型具有任何属性,而是需要控制器/操作级别的某些东西.

@Bilal: I use the same model for Save and Submit and does not want any attributes on Model, rather need something at a controller/action level.

在寻找更好的答案的过程中,我想出了类似的方法.我从另一篇文章中阅读了此文章,但丢失了链接.一旦获得,我将对其进行更新.

In a search to find a better answer I have come up with something like this. I read this from another article, but lost the link. As soon as I get it I will update the same.

添加新的操作过滤器属性

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class IgnoreValidationAttribute : FilterAttribute, IAuthorizationFilter
{
    // TODO: Try to put it on another more appropriate method such as OnActionExcecuting.
    // Looks like - This is the earliest method we can interpret before an action. I really dont like this!
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        //TODO: filterContext != null && filterContext.httpContext != null
        var itemKey = this.CreateKey(filterContext.ActionDescriptor);
        if (!filterContext.HttpContext.Items.Contains(itemKey))
        {
            filterContext.HttpContext.Items.Add(itemKey, true);
        }
    }

    private string CreateKey(ActionDescriptor actionDescriptor)
    {
        var action = actionDescriptor.ActionName.ToLower();
        var controller = actionDescriptor.ControllerDescriptor.ControllerName.ToLower();
        return string.Format("IgnoreValidation_{0}_{1}", controller, action);
    }
}

覆盖DataAnnotationModelMetadata

public class IgnoreValidationModelMetaData : DataAnnotationsModelMetadata
{
    public IgnoreValidationModelMetaData(DataAnnotationsModelMetadataProvider provider, Type containerType,
            Func<object> modelAccessor, Type modelType, string propertyName,
            DisplayColumnAttribute displayColumnAttribute) :
        base(provider, containerType, modelAccessor, modelType, propertyName, displayColumnAttribute)
    {
    }

    public override IEnumerable<ModelValidator> GetValidators(ControllerContext context)
    {
        var itemKey = this.CreateKey(context.RouteData);

        if (context.HttpContext.Items[itemKey] != null && bool.Parse(context.HttpContext.Items[itemKey].ToString()) == true)
        {
            return Enumerable.Empty<ModelValidator>();
        }

        return base.GetValidators(context);
    }

    private string CreateKey(RouteData routeData)
    {
        var action = (routeData.Values["action"] ?? null).ToString().ToLower();
        var controller = (routeData.Values["controller"] ?? null).ToString().ToLower();
        return string.Format("IgnoreValidation_{0}_{1}", controller, action);
    }
}

现在,如果操作方法中存在IgnoreValidationAttribute,请告诉提供者使用我们的自定义数据注释元数据并清空验证

public class IgnoreValidationModelMetaDataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
      Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        var displayColumnAttribute = new List<Attribute>(attributes).OfType<DisplayColumnAttribute>().FirstOrDefault();

        var baseMetaData = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

        // is there any other good strategy to copy the properties?
        return new IgnoreValidationModelMetaData(this, containerType, modelAccessor, modelType, propertyName, displayColumnAttribute)
        {
            TemplateHint = baseMetaData.TemplateHint,
            HideSurroundingHtml = baseMetaData.HideSurroundingHtml,
            DataTypeName = baseMetaData.DataTypeName,
            IsReadOnly = baseMetaData.IsReadOnly,
            NullDisplayText = baseMetaData.NullDisplayText,
            DisplayFormatString = baseMetaData.DisplayFormatString,
            ConvertEmptyStringToNull = baseMetaData.ConvertEmptyStringToNull,
            EditFormatString = baseMetaData.EditFormatString,
            ShowForDisplay = baseMetaData.ShowForDisplay,
            ShowForEdit = baseMetaData.ShowForEdit,
            Description = baseMetaData.Description,
            ShortDisplayName = baseMetaData.ShortDisplayName,
            Watermark = baseMetaData.Watermark,
            Order = baseMetaData.Order,
            DisplayName = baseMetaData.DisplayName,
            IsRequired = baseMetaData.IsRequired
        };
    }
}

用法

[HttpPost]
    [IgnoreValidation]
    public ActionResult SaveDraft(MyModel myModel)
    {
        if (ModelState.IsValid)
        {
            // Should always reach here
        }

        .......
    }

    [HttpPost]
    public ActionResult Submit(MyModel myModel)
    {
        if (ModelState.IsValid)
        {
        }
    }

请不要忘记在您的Application_Start中为联结'ModelMetadataProviders.Current = new IgnoreValidationModelMetaDataProvider();

Please don't forget to call this in your Application_Start for the wireup 'ModelMetadataProviders.Current = new IgnoreValidationModelMetaDataProvider();

尽管有一些担忧.

  1. 与OnAuthorization()相比,有没有一个我们可以较早地操作HttpContext的地方?我不喜欢覆盖此内容以执行与授权无关的想法.请注意,OnActionExecuting()在MVC管道中执行此操作为时已晚(我尝试了此操作,但无法正常工作).

  1. Is there an early place we could manipulate the HttpContext than OnAuthorization() ?. I dont like the idea of overriding this to do something not related to authorization. Please note OnActionExecuting() will be too late in the MVC pipeline to do this(I tried this and is not working).

是否有比将密钥添加到HttpContext并在以后使用的方法更好的方法?

Is there a better way to do this than adding a key to HttpContext and using it later?