3.IMetadataAware接口
用于设置Model元数据自定义属性的AdditionalMetadataAttribute特性实现了一个非常重要的IMetadataAware接口,实现这个接口的特性可以对最终生成的Model元数据进行自由定制。这个接口只有唯一的方法,作为参数的ModelMetadata对象表示可以被自由定制的Model元数据。当表示Model元数据的ModelMetadata对象被创建出来后,前篇帖子里记的一系列注解特性会被提取出来对其进行初始化。
public interface IMetadataAware { void OnMetadataCreated(ModelMetadata metadata); }
接着ASP.NET MVC会获取应用的MetadataAware特性,也就是实现了IMetadataAware接口的特性,并调用它们的OnMetadataCreated方法。我们通过MetadataAware特性不仅可以添加一些额外的元数据属性,还可以修改已经通过数据注解特性初始化的相关属性。ASP.NET MVC定义了2个具体的MetadataAware特性,一个是AdditionalMetadataAttribute,一个是AllowHtmlAttribute。出于安全考虑,ASP.NET MVC在进行Model绑定过程会对请求数据进行验证,以确保没有任何Html标记被包含其中。在ModelMetadata的RequestValidation属性控制着是否开启或关闭请求验证,默认情况下是开启针对html标记的请求验证。如果在数据类型或其数据成员上应用了AllowHtmlAttribute时,则说明允许绑定到目标元素的原始内容包含html标记,也就是忽略请求验证。其实所谓忽略就是在方法里设置RequestValidation属性值为false,还记得OnMetadataCreated方法的参数就为ModelMetadata对象,因此实现是很方便的。
Model元数据的一个主要作用是为定义在HtmlHelper<TModel>中的模板方法提供辅助生成html的元数据信息,这些模板方法只提供被呈现目标数据的模式,比如显示模式或者编辑模式,但具体生成怎样的html页面则由采用的模板来决定,接下来则学习这个。
4.预定义模板
在ASP.NET内部定义了一系列的预定义模板,当我们在View中调用Htmlhelper/HtmlHelper<TModel>的模板方法对Model对象或者它的某个成员进行呈现时,系统会根据当前的呈现模式(也就是显示或者编辑模式)和对应的Model元数据获取一个具体的模板(自定义模板或者预定义模板)。由于Model具有显示和编辑两种呈现模式,所以前面提到的内部预定义模板也是划分为2种基本的类型。
(1)EmailAddress
Email Address模板专用于表示Email地址的字符串类型数据成员,它会将目标元素呈现为一个href属性具有的"mailto:"前缀的链接,这个模板仅仅用于Email地址的显示,因此只在显示模式下有效,或者MVC仅仅定义了基于显示模式的EmailAddress模板。如下代码,我们在Foobar上应用UIHintAttribute特性将模板名称设置为"EmailAddress"。
[UIHint("EmailAddress")] public string Foobar{get;set;} <a href="mailto:[email protected]">[email protected]</a>
如果我们调用HtmlHelper的模板方法将这个属性显示呈现在View中,最终会生成上面的html代码,也就是一个针对Email的链接。
(2)HiddenInput
在前面学习的HiddenInputAttribute特性就是将ModelMetadata对象的TemplateHint属性设置为"HiddenInput"。采用HiddenInput模板,要呈现的数据在显示模式下还是会以文本的形式显示出来,在编辑模式下还会生成一个类型为hidden的<input>元素。不过如果ModelMetadata对象的HideSurroundingHtml属性为true,无论是编辑模式还是显示模式数据内容都不会显示出来。如下所示,在编辑模式下最终呈现的除了属性值的文本,还有一个对应的类型hidden的input元素。
[UIHint("HiddenInput")] public string Foobar{get;set;} {value} //显示模式下 {value}<input id="Foobar" name="Foobar" type="hidden" value="{value}"/> //编辑模式下 //改变HideSurroundingHtml属性后 [HiddenInput(DisplayValue=false)] public string Foobar{get;set;} //呈现的html代码 <input id="Foobar" name="Foobar" type="hidden" value="{value}"/>
(3)Html
当我们需要把含有html标签的数据在界面上原样的呈现出来,我们可以采用Html模板,该模板也是仅仅限于显示模式下。我们为Foo和Bar设置相同的value为一个超链接,如下面所示,加了特性后含有html标签的属性全部输出,而属性Bar则进行了相应的编码。
[UIHint("Html")] public string Foo { get; set; } public string Bar { get; set; } //显示 <a href="baidu.com">baidu</a> //加了特性 <a href="baidu.com">baidu</a> //未加特性
(4)Text
采用text特性后,在显示模式下会直接以文本的形式输出,在编辑模式下对应着一个单行的文本框,如下面代码所示。
[UIHint("Text")] public string Foo { get; set; } {value} //显示模式下 <input class="text-box single-line" id="Foo" name="Foo" type="text" value="value"> //编辑模式下
(5)Url
与EmailAddress和html一样,Url仅限于显示模式。对于表示url的字符串,如果希望它以链接的方式呈现,则可以采用这个模板。
[UIHint("Url")] public string Foo { get; set; } <a href="http://www.baidu.com">http://www.baidu.com</a> //显示模式下
(6)MultilineText、Password和PhoneNumber
这三个特性看名字应该就能猜出内容,第一个特性的作用是在编辑模式下将要编辑的数据呈现为多行的形式,可以理解为text变为textarea;第二个则是让呈现的内容显示为和input里的password的效果一样;第三个则是生成的View里面input属性type为tel。
(7)Collection
Collection模板用于集合对象的显示与编辑,对于采用该模板的集合数据,无论是编辑模式或者显示模式都会遍历其中的每一个元素并根据描述成员的Model元数据决定对其的呈现方式。这里也可以理解为多个属性的呈现,而且在呈现时,如果数据时true或false,则会自动生成html为checkbox。
(8)DateTime、DateTime-local、Date和Time
这4个特性以编辑模式呈现,在View呈现时其input的type属性分别为"datetime","datetime-local","date","time"。