是否可以为.Net Core中的属性中引用的自定义模型绑定程序提供参数? [英] Can I provide parameters to a custom model binder referenced in an attribute in .Net Core?

查看:53
本文介绍了是否可以为.Net Core中的属性中引用的自定义模型绑定程序提供参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个实现IModelBinder的自定义模型绑定程序,并希望使用属性将其应用于我的视图模型.例如:

I have created a custom model binder implementing IModelBinder and want to apply it to my view models using attributes. e.g.:

namespace TestWebApp.Models
{
    public class ExperimentalViewModel
    {
        [ModelBinder(typeof(MandatoryIntBinder))]
        public int MandatoryInt { get; set; }
    }
}

这样做的原因是,如果模型绑定失败,我可以返回自己的错误消息.为了很好地做到这一点,我想为绑定到所使用的MandatoryIntBinder实例的字段传递一个友好的名称.像这样:

The reason for this is so that I can return my own error messages if model binding fails. To do that nicely I would like to pass in a friendly name for the field being bound to the instance of MandatoryIntBinder being used. Something like:

[ModelBinderWithParameters(typeof(MandatoryIntBinder), "Test mandatory int")]

有可能吗?

一种更好的生成自定义模型绑定错误的方法对我来说也是一个很好的解决方案.

A better way of generating custom model binding errors would also be a great solution for me.

推荐答案

我想为绑定到使用的MandatoryIntBinder实例的字段传递一个友好的名称

I would like to pass in a friendly name for the field being bound to the instance of MandatoryIntBinder being used

您将需要以某种方式将其添加到BindingContext中.通常来说,这样做有点麻烦,但是对于传递友好名称的特定情况,已经由您使用的 ModelBinderAttribute 来解决.

You'll need to add it to the BindingContext somehow. Doing this in general is a bit fiddly, but for the specific case of passing in a friendly name, this is already catered for by the ModelBinderAttribute that you're using.

如果您在礼节上指定 Name 属性,如下所示:

If you specify the Name property on the attibute like so:

    namespace TestWebApp.Models
    {
        public class ExperimentalViewModel
        {
            [ModelBinder(typeof(MandatoryIntBinder), Name = "Test Mandatory Int")]
            public int MandatoryInt { get; set; }
        }
    }

然后它将在您的自定义模型绑定器的 BindModelAsync 中作为 bindingContext.BinderModelName 提供.

This will then be available inside your custom model binder's BindModelAsync as bindingContext.BinderModelName.

如果您仍然需要从资料夹内部使用原始模型名称(例如,从表单集合中获取值),则可以通过 bindingContext.ModelMetadata.Name 进行访问bindingContext.ModelMetadata.PropertyName .

If you still need to use the original model name from inside your binder (e.g. to get the value out of the form collection) you can access it via bindingContext.ModelMetadata.Name or bindingContext.ModelMetadata.PropertyName.

我可以为.Net Core中的属性中引用的自定义模型联编程序提供参数吗?

Can I provide parameters to a custom model binder referenced in an attribute in .Net Core?

Name ModelBinderAttribute 上的唯一其他属性.如果要将任意参数传递给各个属性的自定义联编程序,则可以通过其他属性添加它们,可以通过以下方式之一从模型联编程序中访问这些属性:

Name is the only other property on ModelBinderAttribute. If you want to pass arbitrary parameters to your custom binder for individual properties, you can add them via additional attributes, which can be accessed from your model binder in one the following ways:

  1. 在您的自定义活页夹中,将 bindingContext.ModelMetadata 强制转换为 DefaultModelMetadata 并直接访问其 Attributes 属性(这可能很脆弱,但实际上非常不太可能打破)
  2. 实现自己的IMetadataDetailsProvider,它可以查看属性并填充可用于装订器的元数据
  1. Inside your custom binder, cast bindingContext.ModelMetadata to DefaultModelMetadata and access its Attributes property directly (this is arguably brittle, but practically very unlikely to break)
  2. Implement your own IMetadataDetailsProvider, which can look at the attributes and populate the metadata available to the binder

请注意,存在三种IMetadataDetailsProvider: IBindingMetadataProvider IDisplayMetadataProvider IValidationMetadataProvider .

Note that there are three kinds of IMetadataDetailsProvider: IBindingMetadataProvider, IDisplayMetadataProvider, and IValidationMetadataProvider.

  • IBindingMetadataProvider 似乎是最合适的,但它只能填充 BindingMetadata ,后者仅具有少数可写属性(其中大多数可以通过其他属性通过内置的默认元数据提供程序.
  • IDisplayMetadataProvider 可以填充 DisplayMetadata ,其中包含 IDictionary< object,object>.AdditionalValues 属性,可以包含任意数据.请注意,这些数据也将在视图中可用(您可能不需要,但不应引起任何问题).
  • IValidationMetadataProvider 可以填充 ValidationMetadata ,其中包含 IList< object>ValidatorMetadata 属性,可以包含任意数据.显然更适合在验证期间使用,但是它是IList而不是IDictionary的事实使拉回数据的便利性稍差.
  • IBindingMetadataProvider seems like the most appropriate, but it can only populate BindingMetadata which only has a handful of writable properties (most of which can be set by other attributes via the built-in default metadata providers).
  • IDisplayMetadataProvider can populate DisplayMetadata, which contains an IDictionary<object,object> AdditionalValues property, which can contain arbitrary data. Note that this data will also be available in views (which you might not specifically want, but shouldn't cause any problems).
  • IValidationMetadataProvider can populate ValidationMetadata, which contains an IList<object> ValidatorMetadata property, which can contain arbitrary data. Obviously more appropriate for usage during validation, but the fact that it's an IList rather than an IDictionary makes it slightly less convenient to pull data back out.

要使用的 IDisplayMetadataProvider IValidationMetadataProvider 取决于您的确切用例.如果您要支持各种参数集,则 IDisplayMetadataProvider 会启用稍微更多的常规用法.两个接口都支持带有一组固定参数的更强类型的方法.

Which of IDisplayMetadataProvider or IValidationMetadataProvider to use depends on your exact use case. IDisplayMetadataProvider enables slightly more generic usage if you want to support varying sets of parameters. Both interfaces support a more strongly-typed approach with a fixed set of parameters.

您需要一种方法来标识正确的值,以将其从 IList< object>中拉出.ValidatorMetadata 属性.只要将自己限制为自定义属性的单个实例,就可以将属性本身添加到列表中,然后按类型标识它,如下所示:

You need a way to identify the correct value to pull out of the IList<object> ValidatorMetadata property. As long as you limit yourself to a single instance of your custom attribute, you could just add the attribute itself to the list, and then identify it by type, as follows:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false)]
    public class CustomValidationAttribute : Attribute
    {
        public CustomValidationAttribute()
        {
        }

        public string CustomErrorName { get; set; }
    }

提供者

    public class CustomValidationAttributeProvider : IValidationMetadataProvider
    {
        public void CreateValidationMetadata(ValidationMetadataProviderContext context)
        {
            var attribute = context.Attributes.OfType<CustomValidationAttribute>().SingleOrDefault();
            if (attribute != null)
            {
                context.ValidationMetadata.ValidatorMetadata.Add(attribute);
            }
        }
    }

在自定义活页夹中访问

    public class MandatoryIntBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            var customValidationAttribute = bindingContext.ModelMetadata.ValidatorMetadata.OfType<CustomValidationAttribute>().SingleOrDefault();
            return Task.CompletedTask;
        }
    }

在模型上的用法

    [ModelBinder(BinderType = typeof(MandatoryIntBinder))]
    [CustomValidation(CustomErrorName = "Test mandatory int")]
    public int MandatoryInt { get; set; }

提供者注册

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews(options =>
                options.ModelMetadataDetailsProviders.Add(new CustomValidationAttributeProvider()));
        }

使用IDisplayMetadataProvider

如果IDisplayMetadataProvider似乎更适合您的用例,则可以将上述方法与IDisplayMetadataProvider一起使用,而不是IValidatorMetadataProvider.

Using IDisplayMetadataProvider

You can use the above approach with IDisplayMetadataProvider instead of IValidatorMetadataProvider if IDisplayMetadataProvider seems more appropriate for your use case.

IDisplayMetadataProvider填充字典而不是列表的事实也启用了更通用的方法(但是,除非您特别需要此方法,否则最好使用上面更强类型的方法).我们可以通过自定义IDisplayMetadataProvider将通用附加参数传递给自定义模型绑定程序,如下所示:

The fact that IDisplayMetadataProvider populates a dictionary rather than a list also enables a more generic approach (but unless you specifically need this, the more strongly-typed approach above is probably preferable). We can pass generic additional parameters to a custom model binder via a custom IDisplayMetadataProvider as follows:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)]
    public class AdditionalMetadataValueAttribute : Attribute
    {
        public AdditionalMetadataValueAttribute(object key, object value)
        {
            Key = key;
            Value = value;
        }

        public object Key { get; set; }
        public object Value { get; set; }
    }

提供者

    public class AdditionalMetadataValuesProvider : IDisplayMetadataProvider
    {
        public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
        {
            foreach (var attribute in context.Attributes.OfType<AdditionalMetadataValueAttribute>())
            {
                context.DisplayMetadata.AdditionalValues.Add(attribute.Key, attribute.Value);
            }
        }
    }

在自定义活页夹中访问

    public class MandatoryIntBinder : IModelBinder
    {
        public const string ErrorNameKey = "ScratchWebApp.Models.MandatoryIntBinder.ErrorNameKey";
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            var errorName = bindingContext.ModelMetadata.AdditionalValues.GetValueOrDefault(ErrorNameKey) as string;
            return Task.CompletedTask;
        }
    }

在模型上的用法

        [ModelBinder(BinderType = typeof(MandatoryIntBinder))]
        [AdditionalMetadataValue(MandatoryIntBinder.ErrorNameKey, "Test mandatory int")]
        public int MandatoryInt { get; set; }

提供者注册

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews(options =>
            options.ModelMetadataDetailsProviders.Add(new AdditionalMetadataValuesProvider()));
    }


这篇关于是否可以为.Net Core中的属性中引用的自定义模型绑定程序提供参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆