新聞中心
1.驗(yàn)證 Validation

多樣化驗(yàn)證規(guī)則
最常見(jiàn)的驗(yàn)證方式是:在實(shí)體的屬性上加 特性(Attribute) 的方式來(lái)完成基本的數(shù)據(jù)驗(yàn)證. 比如 Required, StringLength, Range 等. 為了保持實(shí)體類的POCO ( Plain Old CLR Objects, 所謂的POCO就是那些不包括INSERT、ADD、DEL等數(shù)據(jù)持久化操作的以及不包括任何業(yè)務(wù)邏輯功能的原始類。只包含最基本的GETTER 和SETTER).,一般是對(duì)實(shí)體類聲明一個(gè)伴隨類(MetadataTypeAttribute),在伴隨類里聲明各種特性.但是伴隨類只能聲明一個(gè)(可以嘗試對(duì)實(shí)體類加多個(gè) MetadataType 看看).
實(shí)際情況下, 不同的系統(tǒng)可能要求不一樣的驗(yàn)證規(guī)則,但是又用的是同一套實(shí)體.
比如旅客信息的郵件地址,電話號(hào)碼等, 在線下系統(tǒng)下是非必填的,但是在線上預(yù)訂的時(shí)候,又是必填的. 你可以定意兩個(gè)不同的實(shí)體. 我采用的是另外一種方法:
先聲明一個(gè)伴隨類:
- public class EContactMetadata {
- [RegularExpression(@"\d{8}" , ErrorMessage = "請(qǐng)輸入8位有效的號(hào)碼")]
- [Required]
- public object PhoneNo1 { get; set; }
- }
在 Global 里把這個(gè)伴隨類注冊(cè)到實(shí)體類上:
- protected void Application_Start() {
- …
- TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(EContact) , typeof(EContactMetadata)) , typeof(EContact));
- …
- }
用這種方式可以對(duì)同一個(gè)實(shí)體類添加多個(gè)伴隨類,后注冊(cè)的會(huì)覆蓋先注冊(cè)的.沒(méi)有發(fā)生覆蓋的會(huì)保留.
部分驗(yàn)證
某個(gè)實(shí)體類的屬性對(duì)應(yīng)到數(shù)據(jù)庫(kù)里的某個(gè)字段是必填的,但是在填寫界面里,又用不到這個(gè)字段,而且這個(gè)字段暫時(shí)也沒(méi)有辦法生成.
比如旅客信息的 OrderNo 是必填的,但是在填寫信息頁(yè)面, OrderNo 還沒(méi)有生成. 所以在 Action 里, ModelState.IsValid 一直是 false. 為了避免這個(gè) false, 你可以新定義一個(gè)類, 但是又不能每一種變化都搞個(gè)類出來(lái)吧.
使用 BindAttribute 接合 ModelBinder
BindAttribute 有 Include 和 Exclude , Include 是只接收 Include 指定的屬性, Exclude 是排除.
- [HttpPost]
- public ActionResult Reserve([Bind(Include = "FG,FB,Hotels,OptionUseDates")]Reserve r , string act) {
- …
意思是參數(shù) r 只接收 Post 過(guò)來(lái)的 FG, FB,Hotels,OptionUseDates 數(shù)據(jù),其它的傳過(guò)來(lái)也不要
在ModelBinder 中把非 Include 的或 Exclude 的驗(yàn)證錯(cuò)誤剔除
- public class SmartModelBinder : DefaultModelBinder {
- protected override void OnModelUpdated(ControllerContext controllerContext , ModelBindingContext bindingContext) {
- Dictionary
startedValid = new Dictionary (StringComparer.OrdinalIgnoreCase); - //獲取模型的驗(yàn)證結(jié)果
- var results = ModelValidator.GetModelValidator(bindingContext.ModelMetadata , controllerContext).Validate(bindingContext.Model);
- foreach(ModelValidationResult validationResult in results) {
- string subPropertyName = CreateSubPropertyName(bindingContext.ModelName , validationResult.MemberName);
- //if(bindingContext.PropertyFilter(subPropertyName)) {
- //bindingContext.PropertyFilter 是一個(gè) delegate, 如果指定的 member 在 BindAttribute 的 Include 的列表內(nèi)(或者非 Exclude 的列表內(nèi)),返回 true, 否則為 false
- //部分驗(yàn)證的功能就是通過(guò)它的結(jié)果來(lái)實(shí)現(xiàn)的
- if(bindingContext.PropertyFilter(validationResult.MemberName)) {
- if(!startedValid.ContainsKey(subPropertyName)) {
- startedValid[subPropertyName] = bindingContext.ModelState.IsValidField(subPropertyName);
- }
- if(startedValid[subPropertyName]) {
- bindingContext.ModelState.AddModelError(subPropertyName , validationResult.Message);
- }
- }
- }
- }
在 Global 里注冊(cè)該 ModelBinder
- protected void Application_Start() {
- …
- ModelBinders.Binders.DefaultBinder = new SmartModelBinder();
- …
自驗(yàn)證 IValidatableObject
某些情況下,簡(jiǎn)單的在用 DataAnnoation 不足以做完業(yè)務(wù)邏輯的驗(yàn)證。比如旅客信息中,兒童的年齡要在返程日期的為準(zhǔn),2 到 11 歲之間, 當(dāng)機(jī)票訂票要求是護(hù)照時(shí),出生日期、護(hù)照號(hào)碼,簽發(fā)國(guó)家、簽發(fā)地為必填;當(dāng)要求是回鄉(xiāng)證時(shí),回鄉(xiāng)證號(hào)必填;當(dāng)要求護(hù)照或回鄉(xiāng)證時(shí),其中之一的信息必須完整。
這種情況下,就需要用到 IValidatableObject , 只要按需求實(shí)現(xiàn) Validate 方法就可以了。
- public class Traveller : IValidatableObject {
- public int TravellerID { get; set; }
- [Required]
- public string FirstName { get; set; }
- [Required]
- public string LastName { get; set; }
- public DateTime Birthday { get; set; }
- public bool IsAdult { get; set; }
- public IEnumerable
Validate(ValidationContext validationContext) { - if(!this.IsAdult && this.Birthday.Date < DateTime.Now.AddYears(-12)) {
- yield return new ValidationResult("兒童年齡必須在 12 歲以內(nèi)");
- }
- }
- }
另外注意,如果標(biāo)注有 DataAnnation 的屬性沒(méi)有驗(yàn)證沒(méi)有通過(guò),是不會(huì)去執(zhí)行 Validate 方法的。
手動(dòng)驗(yàn)證
暫未整理
啟用客服端驗(yàn)證
AppSetting 里
或
- Html.EnableClientValidation(true);
- Html.EnableUnobtrusiveJavaScript(true)
另外,如果以啟用了客戶端驗(yàn)證,但是表單項(xiàng)并沒(méi)有在 Form (Html.BeginForm()) 內(nèi),也是不會(huì)有客戶端驗(yàn)證的.
自定義客戶端驗(yàn)證
具體請(qǐng)參考該 js 文件。
以下所述的客戶端驗(yàn)證都是基于 jQuery.validate 1.9 + jquery.validate.unobtrusive (項(xiàng)目自帶) 的
默認(rèn)的,要在客戶端實(shí)時(shí)看到驗(yàn)證信息(js 驗(yàn)證),需要使用 Html.ValidationMessageFor() 來(lái)生成一個(gè)容器,供 js 往里填寫錯(cuò)誤信息.但是,一個(gè)頁(yè)面如果有很多個(gè)表單對(duì)象,而又不能使用 EditorForModel , 一個(gè)一個(gè)敲,也是一件很頭痛的事。其實(shí)可以通過(guò)修改,來(lái)自動(dòng)生成: 重寫 jQuery validator 的 setting 中的 errorPlacement 和 success 。寫這兩個(gè)是做到最小的修改,如果有必要,你也可以重寫 其它的方法。
- mode1_error 即 errorPlacement
- var mode1_error = function(error, inputElement){
- //查找錯(cuò)誤提示容器
- var container = $("span[data-valmsg-for='" + inputElement[0].name + "']", inputElement[0].form);
- //如果沒(méi)有,生成一個(gè)
- if(container.length == 0){
- container = $("")
- .attr({"data-valmsg-replace":true, "data-valmsg-for":inputElement[0].name}).appendTo(form)
- }
- container.addClass("floatValidationError shadow radius hide");
- //調(diào)用原來(lái)的 errorPlacement
- errorPlacement(error, inputElement);
- //這塊為選擇器做的
- var flow = inputElement;
- var flag = inputElement.attr("data-flag");
- if(flag != undefined){
- container.data("data-for",flag);
- var display = $("*[data-for='" + flag + "']", inputElement[0].form);
- display.addClass("input-validation-error");
- if(display.length > 0){
- flow = display;
- }
- }
- //下拉列表不能設(shè)置border,需要包裝一個(gè) span ,在 span 上顯示border
- if(inputElement[0].tagName == "SELECT" && inputElement.hasClass("input-validation-error") && !inputElement.parent().hasClass("input-validation-error-wrapper")){
- inputElement.wrap("")
- }
- //這一步能過(guò) css hide 做,優(yōu)化性能
- //container.hide();
- flow.focus(elementFocus).blur(elementBlur);
- }
- mode2_success 即 success
- var mode2_success = function(error){
- var container = error.data("unobtrusiveContainer");
- //調(diào)用原有的 success
- success(error);
- //為選擇器所做
- var dataFor = container.data("data-for");
- if(dataFor != undefined){
- $("*[data-for='" + dataFor + "']").removeClass("input-validation-error");
- }
- //如果該error對(duì)應(yīng)的表單對(duì)象是下拉列表,要清除包裝的 span
- var ele = $("[name='" + container.data("valmsg-for") + "']");
- if(ele[0].tagName == "SELECT" && ele.hasClass("input-validation-error")){
- var par = ele.parent();
- ele.appendTo(par.parent());
- par.remove();
- }
- }
使用這個(gè) js 后,即使不Html.ValidationMessageFor() 也一樣會(huì)在客戶端有個(gè)提示
效果如下:
優(yōu)化
如果一個(gè)頁(yè)面里的表單很多,每個(gè)表單又有N個(gè)驗(yàn)證規(guī)則,在 IE7(包括)以下,會(huì)提示:
是否停止運(yùn)行此腳本?
此頁(yè)面上的腳本造成 Internet Explorer 運(yùn)行速度減慢。XXXX
為了該問(wèn)題,測(cè)試打了很多小報(bào)告,我也很煩,jquery validation 造成的問(wèn)題,我有什么辦法,但是問(wèn)題總歸要解決,我花了兩天時(shí)間,用 ie 調(diào)試工具里的探測(cè)器及dynaTrace 收集了很多運(yùn)行數(shù)據(jù),***發(fā)現(xiàn),驗(yàn)證是很快,但是顯示錯(cuò)誤卻很慢,因?yàn)橛写罅康姆荌d查找的 jquery selector。具體表現(xiàn)在 jquery.validate 的 showLabel 方法,和 jquery.validate.unobtrusive 的 onError 方法。我對(duì)這兩個(gè)地方做了修改,在測(cè),整個(gè)驗(yàn)證過(guò)程在 ie7下執(zhí)行的很快!
具體改動(dòng)如下:
jquery.validate.unobtrusive.js
第40行由:
- var container = $(this).find("[data-valmsg-for='" + inputElement[0].name + "']"),
改為:
- var container = $(this).find("span[data-valmsg-for='" + inputElement[0].name + "']"),
目的:減少 dom 節(jié)少遍歷次數(shù)
第48行由:
- error.removeClass("input-validation-error").appendTo(container);
改為:
- error
- //.removeClass("input-validation-error")
- .appendTo(container);
原因:在 jquery.validate 1.9 中的 showLabel 方法的
- var label = this.errorsFor(element)
這個(gè)方法又調(diào) errors() 方法
errors 方法又是跟據(jù) errorClass 去查找
errorClass 即 input-validation-error
在 showLabel 方法里,如果
label = this.errorsFor(element) 沒(méi)有結(jié)果,就又會(huì)新建一個(gè) label (DOM創(chuàng)建)
所以在 jquery.validate.unobtrusive 的第48行不應(yīng)該 removeClass("input-validation-error")
jquery.validate.js 改動(dòng):
showLabel 方法:
在開(kāi)頭處加入
- if (message == undefined)
- return;
errorsFor 方法改為:
- errorsFor: function (element) {
- var name = this.idOrName(element);
- return $(this.settings.errorElement + "." + this.settings.errorClass + "[for='" + name + "']", this.errorContext);
- // return this.errors().filter(function () {
- // return $(this).attr('for') == name;
- // });
- },
修改后的 js 文件:
[[84104]][[84105]]
#p#
擴(kuò)展 DataTypeAttribute
DataTypeAttribute 只是用來(lái)顯示值,而不用于驗(yàn)證(不知是否有誤,請(qǐng)指教)。
但是我就是想要用 DataTypeAttribute 做驗(yàn)證怎么辦呢?自定義一個(gè) DataTypeValidator
- public class DataTypeValidator : DataAnnotationsModelValidator
{ - //和 jQuery 里的email 驗(yàn)證保持一致
- private static readonly string EmailReg = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$";
- private static readonly string UrlReg = @"^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$";
- public DataTypeValidator(ModelMetadata metadata , ControllerContext context , DataTypeAttribute attribute)
- : base(metadata , context , attribute) {
- this.message = attribute.ErrorMessage;
- }
- string message;
- public override IEnumerable
Validate(object container) { - var value = Metadata.Model;
- if(value != null) {
- var dataType = (DataType)Enum.Parse(typeof(DataType) , Metadata.DataTypeName);
- var flag = true;
- switch(dataType) {
- case DataType.EmailAddress:
- if(!Regex.IsMatch(value.ToString() , EmailReg, RegexOptions.IgnoreCase)) {
- flag = false;
- }
- break;
- case DataType.Url:
- if(!Regex.IsMatch(value.ToString() , UrlReg , RegexOptions.IgnoreCase))
- flag = false;
- break;
- }
- if(!flag) {
- yield return new ModelValidationResult() {
- //下面這句不能加,會(huì)影響 SmartModelBinder 里的 CreateSubPropertyName
- //MemberName = Metadata.PropertyName ,
- Message = ErrorMessage
- };
- }
- }
- }
- public override IEnumerable
GetClientValidationRules() { - List
rules = new List (); - ModelClientValidationRule rule;
- switch(Attribute.DataType) {
- case DataType.EmailAddress:
- rule = new ModelClientValidationRule() { ErrorMessage = message , ValidationType = "email" };
- //rule.ValidationParameters.Add("email" , "true");
- rules.Add(rule);
- break;
- case DataType.Url:
- rule = new ModelClientValidationRule() { ErrorMessage = message , ValidationType = "url" };
- //rule.ValidationParameters.Add("url" , "true");
- rules.Add(rule);
- break;
- case DataType.Date:
- rule = new ModelClientValidationRule() { ErrorMessage = message , ValidationType = "date" };
- //rule.ValidationParameters.Add("date" , "true");
- rules.Add(rule);
- break;
- }
- return rules;
- }
- }
然后在 Global 里:
- DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(DataTypeAttribute) , typeof(DataTypeValidator));
該類的方法:GetClientValidationRules 里,將 Date 的 ValidationType 設(shè)為 date 。但是在 jquery.validate 里,date 方法是去驗(yàn)證 new Date(value) 是不是 NaN, 這在 ie 下有問(wèn)題, 比如 new Date("2012-01-01") 是 NaN ,也許你說(shuō)可以用 dateISO 去驗(yàn)證,但是 dateISO 要求如下格式:yyyy-MM-dd 或 yyyy/MM/dd, 換一種格式呢?當(dāng)然是驗(yàn)證不通過(guò)了!
這種情況下,只能重寫 jquery.validate 的 date 驗(yàn)證規(guī)則了(在上文我貼出的 site.js 里有):
- var checkDate = function(value, element){
- return this.optional(element) || value.toDate() != null;
- }
- …
- $.validator.addMethod("date", checkDate);
該部分是覆蓋 jquery.validate 里的 date 處理方法, toDate方法在我貼出的 site.js 里有,請(qǐng)自行參考。另外,我還提供了 DateRange 驗(yàn)證,用以解決復(fù)雜的驗(yàn)證邏輯,用法如下:
- @Html.TextBoxFor(m => m[i].BirthDay , new { Value = Model[i].BirthDay.ToString(dateFmt) , @data_val_required = "必填" , @data_val_dateRange_min = min.ToString(dateFmt) , @data_val_dateRange_max = max.ToString(dateFmt) , @data_val_dateRange = @msg , title = "輸入格式:2012/01/01" })
RequiredIf 驗(yàn)證
做復(fù)雜驗(yàn)證的時(shí)候,可以像上面的 DateRange 的用法一樣去處理大部分問(wèn)題,但是當(dāng)某值為XX時(shí),某XX為必填,DataAnnoation 里沒(méi)提供,jquery.validate 里也沒(méi)有提供, 那只能自定義了:
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
- public class RequiredIfAttribute : ValidationAttribute , IClientValidatable {
- private RequiredAttribute required = new RequiredAttribute();
- ///
- /// 依賴于哪個(gè)屬性
- ///
必須是本對(duì)象下的一個(gè)屬性 - ///
- public string DependentProperty { get; set; }
- private List
- ///
- /// 依賴屬性為哪些值時(shí),該屬性為必填
- ///
- public List
TargetValues { - get {
- return this.targetValues;
- }
- set {
- this.targetValues = value;
- }
- }
- ///
- ///
- ///
- ///
- ///
- public RequiredIfAttribute(string dependentProperty , object targetValue)
- : this(dependentProperty , targetValue , null) {
- }
- public RequiredIfAttribute(string dependentProperty , params object[] targetValues)
- : this(dependentProperty , targetValues , null) {
- }
- ///
- ///
- ///
- ///
- ///
- ///
- public RequiredIfAttribute(string dependentProperty , object targetValue , string errorMessage)
- : base(errorMessage) {
- this.DependentProperty = dependentProperty;
- this.TargetValues.Add(targetValue);
- }
- ///
- ///
- ///
- ///
- ///
- ///
- public RequiredIfAttribute(string dependentProperty , object[] targetValues , string errorMessage)
- : base(errorMessage) {
- this.DependentProperty = dependentProperty;
- this.TargetValues.AddRange(targetValues);
- }
- ///
- ///
- ///
- ///
- ///
- ///
- public IEnumerable
GetClientValidationRules(ModelMetadata metadata , ControllerContext context) { - var rule = new ModelClientValidationRule() {
- ErrorMessage = this.FormatErrorMessage(metadata.GetDisplayName()) ,
- //只能是小寫
- ValidationType = "requiredif" //要在 jquery.validate 里實(shí)現(xiàn) requiredIf 規(guī)則
- };
- var tvs = this.TargetValues.Select(v => {
- if(v.GetType() == typeof(bool))
- return v.ToString().ToLower();
- else
- return v.ToString();
- });
- var ser = new JavaScriptSerializer();
- var values = ser.Serialize(tvs);
- //只能是小寫
- rule.ValidationParameters.Add("dependencyvalue" , values);
- //不要試圖獲取該對(duì)象輸出成 html 的表單前緣,我試了很多方法,都不能獲取,特別是當(dāng) Model 是一個(gè)集合的時(shí)候
- rule.ValidationParameters.Add("dependency" , string.Format("*.{0}" , this.DependentProperty));
- yield return rule;
- }
- ///
- ///
- ///
- ///
- ///
- public override string FormatErrorMessage(string name) {
- if(!String.IsNullOrEmpty(this.ErrorMessageString))
- required.ErrorMessage = this.ErrorMessageString;
- return required.FormatErrorMessage(name);
- }
- ///
- ///
- ///
- ///
- ///
- ///
- protected override ValidationResult IsValid(object value , ValidationContext validationContext) {
- var containerType = validationContext.ObjectInstance.GetType();
- var field = containerType.GetProperty(this.DependentProperty);
- if(field == null)
- throw new MissingMemberException(containerType.Name , this.DependentProperty);
- var dependentvalue = field.GetValue(validationContext.ObjectInstance , null);
- if((dependentvalue == null && (this.TargetValues == null || this.TargetValues.Count == 0)) || (dependentvalue != null && this.TargetValues.Any(t => t.Equals(dependentvalue)))) {
- i(!required.IsValid(value))
- return new ValidationResult(FormatErrorMessage(validationContext.DisplayName) , new[] { validationContext.MemberName });
- }
- return ValidationResult.Success;
- }
- Gloal
- DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredAttribute) , typeof(MyRequiredAttributeAdapter));
擴(kuò)展 jquery.validate
處理方法:
- var requiredIf = function (value, element, params) {
- var prefix = getModelPrefix(element.name);
- var fullDependencyName = appendModelPrefix(params["dependency"], prefix);
- var dependency = $(element.form).find(":input[name='" + fullDependencyName + "']");
- var dependencyValue = params["dependencyvalue"];
- var acturalValue = null;
- if(dependency.attr("type") == "checkbox"){
- acturalValue = dependency.attr("checked") == true ? dependency.val() : null;
- }else{
- acturalValue = dependency.val();
- }
- eval("dvs = " + dependencyValue);
- if(dvs.indexOf2(acturalValue) >= 0){
- return $.validator.methods.required.call(this, value, element, params);
- }
- return true;
- }
- Adapter
- var requiredIfAdapter = function (options) {
- options.rules["requiredIf"] = options.params;
- options.messages["requiredIf"] = options.message;
- }
注冊(cè):
- $.validator.addMethod('requiredIf',requiredIf);
- $.validator.unobtrusive.adapters.add('requiredIf',['dependency', 'dependencyvalue'],requiredIfAdapter);
使用:
- …
- [RequiredIf("CertificateType" , 2)]
- public object Nation { get; set; }
- [RequiredIf("CertificateType" , 1 , 2)]
- public object IDNumber { get; set; }
- …
RequiredIf 所涉及到的 js 方法同樣在 site.js 里也有提供
處理中英文混合的 RequiredAttribute 提示
將項(xiàng)目部署到服務(wù)器上,有個(gè)奇怪的現(xiàn)象,Required 的提示居然是中英文混合的(和 jQuery 無(wú)關(guān),因?yàn)?required 的錯(cuò)誤信息是寫在 html 里的),其原因是,設(shè)置了 DisplayAttribute ,也設(shè)置了 Required, 并且也帶 ErrorMessage (是通過(guò) ErrorMessageResourceType 指向其它DLL里的,我沒(méi)有引用這個(gè)DLL,也不推薦這樣做)。
要解決這個(gè)問(wèn)題,只能擴(kuò)展 RequiredAttributeAdapter 了
- public class MyRequiredAttributeAdapter : RequiredAttributeAdapter {
- public MyRequiredAttributeAdapter(ModelMetadata metadata , ControllerContext context , RequiredAttribute attribute)
- : base(metadata , context , attribute) {
- //ErrorMessage 和 ErrorMessageResourceName 不可同時(shí)存在
- if(attribute.ErrorMessage == null) {
- //attribute.ErrorMessage = null;
- attribute.ErrorMessageResourceType = typeof(CustomMessage);
- attribute.ErrorMessageResourceName = "PropertyValueRequired";
- }
- }
- }
其中,ErrorMessage 和 ErrorMessageResourceName 不能同時(shí)存在,這個(gè)需要注意。
然后注冊(cè)這個(gè)適配器:
- Global
- DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredAttribute) , typeof(MyRequiredAttributeAdapter));
在處理這個(gè)問(wèn)題的時(shí)候,我搜索到另外一個(gè)問(wèn)題:PropertyValueInvalid 的本地化
1, 在站點(diǎn)下新建一個(gè)全局資源文件夾: App_GlobalResources
2,新建一個(gè)資源文件:CustomMessage.rexs (名隨便)
3, 加一個(gè)字符串資源:PropertyValueInvalid, 值為:{0} 不是有效的 {1}
4, Global 里:
DefaultModelBinder.ResourceClassKey = "CustomMessage";
CustomMessage 即第二步建的資源文件
RedirectToAction 后,ModelState 丟了!
假如說(shuō)你的某個(gè)action 往 ViewBag 里寫了好多東西進(jìn)去。當(dāng)這個(gè) action 的 post 方法發(fā)生,并且 model 驗(yàn)證不通過(guò),為了不在寫那么多 ViewBag , 你必定會(huì)用到 RedirectToAction, 但是這一 Redirect ,你的驗(yàn)證錯(cuò)誤就丟了,用戶輸入的內(nèi)容也沒(méi)有了,redirect 的結(jié)果同新打開(kāi)的頁(yè)面一樣,看不到錯(cuò)誤提示,看不到用戶輸入的內(nèi)容,咋辦呢?在 post 方法里寫 ViewBag ?避免使用 RedirectToAction ?
重寫 Controller 的 OnActionExecuted
- protected override void OnActionExecuted(ActionExecutedContext filterContext) {
- if(TempData["ModelState"] != null && !ModelState.Equals(TempData["ModelState"]))
- ModelState.Merge((ModelStateDictionary)TempData["ModelState"]);
- base.OnActionExecuted(filterContext);
- }
當(dāng)ModelState.IsValid 為 false 時(shí):
- if(this.ModelState.IsValid) {
- ...
- } else {
- ...
- this.TempData.Add("ModelState" , this.ModelState);
- return RedirectToAction("Infomation" , new { booking = "direct" });
- }
#p#
頁(yè)面 Html
Javascript ***出現(xiàn)在 body 結(jié)束的地方
如果你隨意放置 javascript 代碼在頁(yè)面里,會(huì)造成什么后果?這個(gè)我就不說(shuō)了,反正按我的規(guī)則,我都是把 javascript 代碼統(tǒng)一放到 body 結(jié)束的地方。在 MVC 里有神馬辦法能做到這個(gè)統(tǒng)一?搜索了很多,***我使用了 ViewContext.HttpContext
- ///
- /// 注冊(cè)腳本
- ///
- ///
- ///
- ///
- public static MvcHtmlString Script(this HtmlHelper htmlHelper , Func
template) { - htmlHelper.ViewContext.HttpContext.Items["_script_" + Guid.NewGuid()] = template;
- return MvcHtmlString.Empty;
- }
- ///
- /// 渲染腳本
- ///
- ///
- ///
- public static IHtmlString RenderScripts(this HtmlHelper htmlHelper) {
- foreach(object key in htmlHelper.ViewContext.HttpContext.Items.Keys) {
- if(key.ToString().StartsWith("_script_")) {
- var template = htmlHelper.ViewContext.HttpContext.Items[key] as Func
; - if(template != null) {
- htmlHelper.ViewContext.Writer.Write(template(null));
- }
- }
- }
- return MvcHtmlString.Empty;
- }
在 _Layout.cshtml 里:
- ....
- @Html.RenderScripts()


咨詢
建站咨詢