博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ASP.NET MVC Model元数据及其定制:一个重要的接口IMetadataAware
阅读量:7104 次
发布时间:2019-06-28

本文共 5584 字,大约阅读时间需要 18 分钟。

在介绍用于自定义Model元数据属性的AdditionalMetadataAttribute特性时我们提到了它实现的接口IMedataAware,我们说这是一个非常重要并且有用的接口,通过自定义实现该接口的特性我们可以对最终生成的Model元数据进行自由地定制。如下面的代码片断所示,IMedataAware接口具有唯一的方法成员OnMetadataCreated。当Model元数据被创建出来后,会先获取上述的这一系列标注特性对其进行初始化,然后获取应用在目标元素上所有实现了IMedataAware接口的特性,并将初始化的ModelMetadata对象作为参数调用OnMetadataCreated方法。所以我们通过创建实现该接口的特性不仅仅可以添加一些额外的元数据属性,也可以修改已经通过相应的标注特性初始化的相关属性。[本文已经同步到《》中]

1: public interface IMetadataAware
2: {
3:     void OnMetadataCreated(ModelMetadata metadata);
4: }

ASP.NET MVC定义了两个实现了IMedataAware接口的特性,一个就是我们已经介绍过的AdditionalMetadataAttribute,另一个则是。

一、AllowHtmlAttribute

为了防止最终用于通过在针对某个数据的输入中注入一些HTML来攻击我们的Web应用,ASP.NET MVC在进行Model绑定之前会对对应的请求数据进行验证,确保没有任何HTML标记包含其。这个针对HTML标记的验证通过ModelMetadata的RequestValidationEnabled来控制,如下面的代码片断所示,这是一个布尔类型的可读写属性。该属性在默认情况下为True,意味着默认开启针对HTML标记的请求验证。

1: public class ModelMetadata
2: {
3:     //其他成员
4:     public virtual bool RequestValidationEnabled { get; set; }
5: }

AllowHtmlAttribute特性,顾名思义,就是运行作为目标元素的内容包含HTML标记。如下面的代码片断所示,AllowHtmlAttribute是实现了IMetadataAware 接口,在OnMetadataCreated方法中它直接将作为参数的ModelMetadata对象的RequestValidationEnabled属性设置为False,从而使针对目标对象的请求验证被忽略掉。

1: [AttributeUsage(AttributeTargets.Property, AllowMultiple=false, Inherited=true)]
2: public sealed class AllowHtmlAttribute : Attribute, IMetadataAware
3: {
4:     public void OnMetadataCreated(ModelMetadata metadata)
5:     {
6:         //其他操作
7:         metadata.RequestValidationEnabled = false;
8:     }
9: }

为了验证ASP.NET MVC针对HTML标记的请求验证和AllowHtmlAttribute的作用,我们来做一个简单的实例演示。在通过Visual Studio提供的ASP.NET MVC项目模板创建的空Web应用中,我们定义了如下一个数据类型Foo,其中属性Baz上应用了AllowHtmlAttribute特性。

1: public class Foo
2: {
3:     public string Bar { get; set; }
4: 
5:     [AllowHtml]
6:     public string Baz { get; set; }
7: }

然后我们创建如下一个默认的HomeController,默认的Index操作方法中具有一个类型为Foo的参数,该参数直接作为Model呈现在默认的View中。

1: public class HomeController : Controller
2: {
3:     public ActionResult Index(Foo foo)
4:     {
5:         return View(foo);
6:     }
7: }

如下所示的Index操作对应的View定义,这是一个以Foo为Model的强类型View。在该View中,我们直接调用HtmlHelper<Model>的EditorForModel方法将Foo对象以编辑模式呈现出来。

1: @model Foo
2: @{
3:     ViewBag.Title = "Index";
4: }
5: @Html.EditorForModel()

现在我们直接运行该Web应用。根据Model绑定的规则我们知道,如果我们通过浏览器访问HomeController的Index操作,可以通过查询字符串的方式对该操作方法的参数进行初始化。具体来说,我们可以分别指定名称为Bar和Baz的查询字符串对作为参数的Foo对象的两个属性进行初始化。为了验证对包含HTML标记的输入的验证,我们将最终绑定到Model上的查询字符串设置为<script/>。

如下图所示,由于Foo的属性Baz上应用了AllowHtmlAttribute特性是之支持包含HTML标记的数据,所以我们以查询字符串方式指定的包含HTML标记的内容(<script/>)直接显示在相应的文本框中。但是Bar属性在默认情况下是不运行绑定的数据具有任何HTML标记的,所以会将输入的数据视为恶意注入的HTML,直接抛出异常。

二、实例演示:创建实现IMetadataAware接口的特性定制Model元数据

通过上面对Model元数据定义的介绍我们知道显示名称可以通过在数据类型或者属性成员上应用DisplayAttribute特性来定义。在使用该特性的时候,我们需要显式制定表示显示名称的Name属性,如果需要进行本地化处理,需要将显示内容定义在某个资源文件中,并通过ResourceType属性指定该资源文件生成的类型。[源代码从下载]

为了简化,我们通过实现IMetadataAware接口的方式定义了如下一个DisplayTextAttribute特性。该特性的属性DisplayName/ResourceType与DisplayAttribute的Name/ResourceType具有相同的作用,唯一不同的是DisplayTextAttribute的这两个属性均是可以缺省的。如果DisplayName没有显式指定,则默认使用属性名称或者类型名称;如果ResourceType没有显式指定,则采用通过静态字段staticResourceType表示的默认资源类型,该类型通过静态方法SetResourceType进行注册。

1: [AttributeUsage(AttributeTargets.Class| AttributeTargets.Property)]
2: public class DisplayTextAttribute: Attribute, IMetadataAware
3: {
4:     private static Type staticResourceType;
5:     public string DisplayName { get; set; }
6:     public Type ResourceType { get; set; }
7: 
8:     public DisplayTextAttribute()
9:     {
10:         this.ResourceType = staticResourceType;
11:     }
12: 
13:     public void OnMetadataCreated(ModelMetadata metadata)
14:     {
15:         this.DisplayName = this.DisplayName ?? (metadata.PropertyName ?? metadata.ModelType.Name);
16:         if (null == this.ResourceType)
17:         {
18:             metadata.DisplayName = this.DisplayName;
19:             return;
20:         }
21:         PropertyInfo property = this.ResourceType.GetProperty(this.DisplayName, BindingFlags.NonPublic|BindingFlags.Public| BindingFlags.Static);
22:         metadata.DisplayName = property.GetValue(null, null).ToString();
23:     }
24: 
25:     public static void SetResourceType(Type resourceType)
26:     {
27:         staticResourceType = resourceType;
28:     }
29: }

DisplayTextAttribute对Model元数据的定制实现在OnMetadataCreated方法中。具体来说,我们根据设置的DisplayName和ResourceType属性解析出最终作为目标元素显示名称的文本作为ModelMetadata的DisplayName属性值。

接下来我们来演示如何使用这个DisplayTextAttribute特性来替换DisplayAttribute特性进行显示名称的设置,为此我们在通过Visual Studio的ASP.NET MVC 项目模板创建的空Web应用中创建如下一个表示员工的Employee类型。Employee所有的属性上均应用了DisplayTextAttribute特性,而DisplayName和ReourceType属性没有显式指定。

1: public class Employee
2: {
3:     [DisplayText]
4:     public string Name { get; set; }
5: 
6:     [DisplayText]
7:     public string Gender { get; set; }
8: 
9:     [DisplayText]
10:     [DataType(DataType.Date)]
11:     public DateTime BirthDate { get; set; }
12: 
13:     [DisplayText]
14:     public string Department { get; set; }
15: }

接下来我们打开项目的属性对话框并选择“资源(Rources)”Tab页,按照如下图所示为Employee中的四个属性定义相应的资源字符串作为显示的名称,资源字符串条目的名称为属性名。

该资源文件会自动生成一个类型为Resources的内部类型。由于应用在Employee属性上的DisplayTextAttribute特性并没有显式指定资源类型,所以我们需要在Global.asax文件中通过如下的方式将Resources类型注册为默认的资源类型。

1: public class MvcApplication : System.Web.HttpApplication
2: {
3:     //其他成员
4:     protected void Application_Start()
5:     {
6:         //其他操作
7:         DisplayTextAttribute.SetResourceType(typeof(Resources));
8:     }
9: }

现在我们通过调用HtmlHelper<TModel>的EditorForModel方法将一个具体的Employee对象以编辑模式显示在某个Model类型为Employee的强类型View上,会呈现出如下图所示的效果,我们可以看到作为标签显示的文字正式我们定义在资源文件中的内容。

作者:蒋金楠
微信公众账号:大内老A
微博:
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号
蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
你可能感兴趣的文章
softether *** ubuntu部署
查看>>
我的友情链接
查看>>
云盾防Ddos文献之应对篇 ——DDoS防御方案
查看>>
我的友情链接
查看>>
64位操作系统不能安装64位虚拟机的解决办法
查看>>
怎样在log4j.xml配置文件中引入变量:小公司经验较多的我和阿里UC等大公司经验较多的Boss,一些技术交流和探讨...
查看>>
OpenJDK源码研究笔记(五)-缓存Integer等类型的频繁使用的数据和对象,大幅度提升性能(一道经典的Java笔试题)...
查看>>
什么是自然语言处理--学习笔记
查看>>
网闸中的核心技术
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
pythonic
查看>>
Ubuntu 配置 Android开发 adb调试
查看>>
OSX光标移动迟钝的原因
查看>>
Openstack Mitaka for Centos7.2 部署指南(三)
查看>>
Servlet的生命周期
查看>>
技术年货:美团技术沙龙合辑大放送——85个演讲,70+小时视频
查看>>
在人生方向感没那么强的时候,怎么更好地生活下去
查看>>
递归读取文件夹文件
查看>>
Select 1 from table 分析
查看>>