日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
MVC3項(xiàng)目總結(jié)

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è)伴隨類:

 
 
 
 
  1. public class EContactMetadata {  
  2.  
  3. [RegularExpression(@"\d{8}" , ErrorMessage = "請(qǐng)輸入8位有效的號(hào)碼")]  
  4.  
  5. [Required]  
  6.  
  7. public object PhoneNo1 { get; set; }  
  8.  
  9. }  

在 Global 里把這個(gè)伴隨類注冊(cè)到實(shí)體類上:

 
 
 
 
  1. protected void Application_Start() {  
  2.  
  3. …  
  4.  
  5. TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(EContact) , typeof(EContactMetadata)) , typeof(EContact));  
  6.  
  7. …  
  8.  
  9. }  

用這種方式可以對(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 是排除.

 
 
 
 
  1. [HttpPost]  
  2.  
  3. public ActionResult Reserve([Bind(Include = "FG,FB,Hotels,OptionUseDates")]Reserve r , string act) {  
  4.  
  5. …  

意思是參數(shù) r 只接收 Post 過(guò)來(lái)的 FG, FB,Hotels,OptionUseDates 數(shù)據(jù),其它的傳過(guò)來(lái)也不要

在ModelBinder 中把非 Include 的或 Exclude 的驗(yàn)證錯(cuò)誤剔除

 
 
 
 
  1. public class SmartModelBinder : DefaultModelBinder {  
  2. protected override void OnModelUpdated(ControllerContext controllerContext , ModelBindingContext bindingContext) {  
  3. Dictionary startedValid = new Dictionary(StringComparer.OrdinalIgnoreCase);  
  4. //獲取模型的驗(yàn)證結(jié)果  
  5. var results = ModelValidator.GetModelValidator(bindingContext.ModelMetadata , controllerContext).Validate(bindingContext.Model);  
  6. foreach(ModelValidationResult validationResult in results) {  
  7. string subPropertyName = CreateSubPropertyName(bindingContext.ModelName , validationResult.MemberName);  
  8. //if(bindingContext.PropertyFilter(subPropertyName)) {  
  9. //bindingContext.PropertyFilter 是一個(gè) delegate, 如果指定的 member 在 BindAttribute 的 Include 的列表內(nèi)(或者非 Exclude 的列表內(nèi)),返回 true, 否則為 false  
  10. //部分驗(yàn)證的功能就是通過(guò)它的結(jié)果來(lái)實(shí)現(xiàn)的  
  11. if(bindingContext.PropertyFilter(validationResult.MemberName)) {  
  12. if(!startedValid.ContainsKey(subPropertyName)) {  
  13. startedValid[subPropertyName] = bindingContext.ModelState.IsValidField(subPropertyName);  
  14. }  
  15. if(startedValid[subPropertyName]) {  
  16. bindingContext.ModelState.AddModelError(subPropertyName , validationResult.Message);  
  17. }  
  18. }  
  19. }  
  20. }  

在 Global 里注冊(cè)該 ModelBinder

 
 
 
 
  1. protected void Application_Start() {  
  2. …  
  3. ModelBinders.Binders.DefaultBinder = new SmartModelBinder();  
  4. … 

自驗(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 方法就可以了。

 
 
 
 
  1. public class Traveller : IValidatableObject {  
  2. public int TravellerID { get; set; }  
  3. [Required]  
  4. public string FirstName { get; set; }  
  5. [Required]  
  6. public string LastName { get; set; }  
  7. public DateTime Birthday { get; set; }  
  8. public bool IsAdult { get; set; }  
  9. public IEnumerable Validate(ValidationContext validationContext) {  
  10. if(!this.IsAdult && this.Birthday.Date < DateTime.Now.AddYears(-12)) {  
  11. yield return new ValidationResult("兒童年齡必須在 12 歲以內(nèi)");  
  12. }  
  13. }  

另外注意,如果標(biāo)注有 DataAnnation 的屬性沒(méi)有驗(yàn)證沒(méi)有通過(guò),是不會(huì)去執(zhí)行 Validate 方法的。

手動(dòng)驗(yàn)證

暫未整理

啟用客服端驗(yàn)證

AppSetting 里

 
 
 
 
  1.  
  2.  
  3.  
  4.  

 
 
 
 
  1. Html.EnableClientValidation(true);  
  2. 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è)是做到最小的修改,如果有必要,你也可以重寫 其它的方法。

 
 
 
 
  1. mode1_error 即 errorPlacement  
  2. var mode1_error = function(error, inputElement){  
  3. //查找錯(cuò)誤提示容器  
  4. var container = $("span[data-valmsg-for='" + inputElement[0].name + "']", inputElement[0].form);  
  5. //如果沒(méi)有,生成一個(gè)  
  6. if(container.length == 0){  
  7. container = $("")  
  8. .attr({"data-valmsg-replace":true, "data-valmsg-for":inputElement[0].name}).appendTo(form)  
  9. }  
  10. container.addClass("floatValidationError shadow radius hide");  
  11. //調(diào)用原來(lái)的 errorPlacement  
  12. errorPlacement(error, inputElement);  
  13. //這塊為選擇器做的  
  14. var flow = inputElement;  
  15. var flag = inputElement.attr("data-flag");  
  16. if(flag != undefined){  
  17. container.data("data-for",flag);  
  18. var display = $("*[data-for='" + flag + "']", inputElement[0].form);  
  19. display.addClass("input-validation-error");  
  20. if(display.length > 0){  
  21. flow = display;  
  22. }  
  23. }  
  24. //下拉列表不能設(shè)置border,需要包裝一個(gè) span ,在 span 上顯示border  
  25. if(inputElement[0].tagName == "SELECT" && inputElement.hasClass("input-validation-error") && !inputElement.parent().hasClass("input-validation-error-wrapper")){  
  26. inputElement.wrap("")  
  27. }  
  28. //這一步能過(guò) css hide 做,優(yōu)化性能  
  29. //container.hide();  
  30. flow.focus(elementFocus).blur(elementBlur);  
  31. }  
  32. mode2_success 即 success  
  33. var mode2_success = function(error){  
  34. var container = error.data("unobtrusiveContainer");  
  35. //調(diào)用原有的 success  
  36. success(error);  
  37. //為選擇器所做  
  38. var dataFor = container.data("data-for");  
  39. if(dataFor != undefined){  
  40. $("*[data-for='" + dataFor + "']").removeClass("input-validation-error");  
  41. }  
  42. //如果該error對(duì)應(yīng)的表單對(duì)象是下拉列表,要清除包裝的 span   
  43. var ele = $("[name='" + container.data("valmsg-for") + "']");  
  44. if(ele[0].tagName == "SELECT" && ele.hasClass("input-validation-error")){  
  45. var par = ele.parent();  
  46. ele.appendTo(par.parent());  
  47. par.remove();  
  48. }  

使用這個(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行由:

 
 
 
 
  1. var container = $(this).find("[data-valmsg-for='" + inputElement[0].name + "']"), 

改為:

 
 
 
 
  1. var container = $(this).find("span[data-valmsg-for='" + inputElement[0].name + "']"), 

目的:減少 dom 節(jié)少遍歷次數(shù)

第48行由:

 
 
 
 
  1. error.removeClass("input-validation-error").appendTo(container); 

改為:

 
 
 
 
  1. error  
  2. //.removeClass("input-validation-error")  
  3. .appendTo(container); 

原因:在 jquery.validate 1.9 中的 showLabel 方法的

 
 
 
 
  1. 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)頭處加入

 
 
 
 
  1. if (message == undefined)  
  2. return; 

errorsFor 方法改為:

 
 
 
 
  1. errorsFor: function (element) {  
  2. var name = this.idOrName(element);  
  3. return $(this.settings.errorElement + "." + this.settings.errorClass + "[for='" + name + "']", this.errorContext);  
  4. // return this.errors().filter(function () {  
  5. // return $(this).attr('for') == name;  
  6. // });  
  7. }, 

修改后的 js 文件:

[[84104]][[84105]]

#p#

擴(kuò)展 DataTypeAttribute

DataTypeAttribute 只是用來(lái)顯示值,而不用于驗(yàn)證(不知是否有誤,請(qǐng)指教)。

但是我就是想要用 DataTypeAttribute 做驗(yàn)證怎么辦呢?自定義一個(gè) DataTypeValidator

 
 
 
 
  1. public class DataTypeValidator : DataAnnotationsModelValidator {  
  2.  
  3. //和 jQuery 里的email 驗(yàn)證保持一致  
  4.  
  5. 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])))\.?$";  
  6.  
  7. 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})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$";  
  8.  
  9. public DataTypeValidator(ModelMetadata metadata , ControllerContext context , DataTypeAttribute attribute)  
  10. : base(metadata , context , attribute) {  
  11. this.message = attribute.ErrorMessage;  
  12. }  
  13. string message;  
  14. public override IEnumerable Validate(object container) {  
  15. var value = Metadata.Model;  
  16. if(value != null) {  
  17. var dataType = (DataType)Enum.Parse(typeof(DataType) , Metadata.DataTypeName);  
  18. var flag = true;  
  19. switch(dataType) {  
  20. case DataType.EmailAddress:  
  21. if(!Regex.IsMatch(value.ToString() , EmailReg, RegexOptions.IgnoreCase)) {  
  22. flag = false;  
  23. }  
  24. break;  
  25. case DataType.Url:  
  26. if(!Regex.IsMatch(value.ToString() , UrlReg , RegexOptions.IgnoreCase))  
  27. flag = false;  
  28. break;  
  29. }  
  30. if(!flag) {  
  31. yield return new ModelValidationResult() {  
  32. //下面這句不能加,會(huì)影響 SmartModelBinder 里的 CreateSubPropertyName  
  33. //MemberName = Metadata.PropertyName ,  
  34. Message = ErrorMessage 
  35. };  
  36. }  
  37. }  
  38. }  
  39. public override IEnumerable GetClientValidationRules() {  
  40. List rules = new List();  
  41. ModelClientValidationRule rule;  
  42. switch(Attribute.DataType) {  
  43. case DataType.EmailAddress:  
  44. rule = new ModelClientValidationRule() { ErrorMessage = message , ValidationType = "email" };  
  45. //rule.ValidationParameters.Add("email" , "true");  
  46. rules.Add(rule);  
  47. break;  
  48. case DataType.Url:  
  49. rule = new ModelClientValidationRule() { ErrorMessage = message , ValidationType = "url" };  
  50. //rule.ValidationParameters.Add("url" , "true");  
  51. rules.Add(rule);  
  52. break;  
  53. case DataType.Date:  
  54. rule = new ModelClientValidationRule() { ErrorMessage = message , ValidationType = "date" };  
  55. //rule.ValidationParameters.Add("date" , "true");  
  56. rules.Add(rule);  
  57. break;  
  58. }  
  59. return rules;  
  60. }  

然后在 Global 里:

 
 
 
 
  1. 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 里有):

 
 
 
 
  1. var checkDate = function(value, element){  
  2. return this.optional(element) || value.toDate() != null;  
  3. }  
  4. …  
  5. $.validator.addMethod("date", checkDate); 

該部分是覆蓋 jquery.validate 里的 date 處理方法, toDate方法在我貼出的 site.js 里有,請(qǐng)自行參考。另外,我還提供了 DateRange 驗(yàn)證,用以解決復(fù)雜的驗(yàn)證邏輯,用法如下:

 
 
 
 
  1. @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)有提供, 那只能自定義了:

 
 
 
 
  1. [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]  
  2. public class RequiredIfAttribute : ValidationAttribute , IClientValidatable {  
  3.  
  4. private RequiredAttribute required = new RequiredAttribute();  
  5.  
  6. ///   
  7. /// 依賴于哪個(gè)屬性  
  8. /// 必須是本對(duì)象下的一個(gè)屬性  
  9. ///   
  10. public string DependentProperty { get; set; }  
  11.  
  12. private List targetValues = new List();  
  13. ///   
  14. /// 依賴屬性為哪些值時(shí),該屬性為必填  
  15. ///   
  16. public List TargetValues {  
  17. get {  
  18. return this.targetValues;  
  19. }  
  20. set {  
  21. this.targetValues = value;  
  22. }  
  23. }  
  24.  
  25. ///   
  26. ///   
  27. ///   
  28. ///   
  29. ///   
  30. public RequiredIfAttribute(string dependentProperty , object targetValue)  
  31. : this(dependentProperty , targetValue , null) {  
  32. }  
  33.  
  34. public RequiredIfAttribute(string dependentProperty , params object[] targetValues)  
  35. : this(dependentProperty , targetValues , null) {  
  36. }  
  37.  
  38. ///   
  39. ///   
  40. ///   
  41. ///   
  42. ///   
  43. ///   
  44. public RequiredIfAttribute(string dependentProperty , object targetValue , string errorMessage)  
  45. : base(errorMessage) {  
  46. this.DependentProperty = dependentProperty;  
  47. this.TargetValues.Add(targetValue);  
  48. }  
  49.  
  50. ///   
  51. ///   
  52. ///   
  53. ///   
  54. ///   
  55. ///   
  56. public RequiredIfAttribute(string dependentProperty , object[] targetValues , string errorMessage)  
  57. : base(errorMessage) {  
  58. this.DependentProperty = dependentProperty;  
  59. this.TargetValues.AddRange(targetValues);  
  60. }  
  61.  
  62. ///   
  63. ///   
  64. ///   
  65. ///   
  66. ///   
  67. ///   
  68. public IEnumerable GetClientValidationRules(ModelMetadata metadata , ControllerContext context) {  
  69. var rule = new ModelClientValidationRule() {  
  70. ErrorMessage = this.FormatErrorMessage(metadata.GetDisplayName()) ,  
  71. //只能是小寫  
  72. ValidationType = "requiredif" //要在 jquery.validate 里實(shí)現(xiàn) requiredIf 規(guī)則  
  73. };  
  74.  
  75. var tvs = this.TargetValues.Select(v => {  
  76. if(v.GetType() == typeof(bool))  
  77. return v.ToString().ToLower();  
  78. else 
  79. return v.ToString();  
  80. });  
  81.  
  82. var ser = new JavaScriptSerializer();  
  83. var values = ser.Serialize(tvs);  
  84. //只能是小寫  
  85. rule.ValidationParameters.Add("dependencyvalue" , values);  
  86. //不要試圖獲取該對(duì)象輸出成 html 的表單前緣,我試了很多方法,都不能獲取,特別是當(dāng) Model 是一個(gè)集合的時(shí)候  
  87. rule.ValidationParameters.Add("dependency" , string.Format("*.{0}" , this.DependentProperty));  
  88. yield return rule;  
  89. }  
  90. ///   
  91. ///   
  92. ///   
  93. ///   
  94. ///   
  95. public override string FormatErrorMessage(string name) {  
  96. if(!String.IsNullOrEmpty(this.ErrorMessageString))  
  97. required.ErrorMessage = this.ErrorMessageString;  
  98. return required.FormatErrorMessage(name);  
  99. }  
  100. ///   
  101. ///   
  102. ///   
  103. ///   
  104. ///   
  105. ///   
  106. protected override ValidationResult IsValid(object value , ValidationContext validationContext) {  
  107. var containerType = validationContext.ObjectInstance.GetType();  
  108. var field = containerType.GetProperty(this.DependentProperty);  
  109. if(field == null)  
  110. throw new MissingMemberException(containerType.Name , this.DependentProperty);  
  111. var dependentvalue = field.GetValue(validationContext.ObjectInstance , null);  
  112. if((dependentvalue == null && (this.TargetValues == null || this.TargetValues.Count == 0)) || (dependentvalue != null && this.TargetValues.Any(t => t.Equals(dependentvalue)))) {  
  113. i(!required.IsValid(value))  
  114. return new ValidationResult(FormatErrorMessage(validationContext.DisplayName) , new[] { validationContext.MemberName });  
  115. }  
  116. return ValidationResult.Success;  
  117. }  
  118. Gloal  
  119. DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredAttribute) , typeof(MyRequiredAttributeAdapter)); 
  120. 擴(kuò)展 jquery.validate

    處理方法:

     
     
     
     
    1. var requiredIf = function (value, element, params) {  
    2. var prefix = getModelPrefix(element.name);  
    3. var fullDependencyName = appendModelPrefix(params["dependency"], prefix);  
    4. var dependency = $(element.form).find(":input[name='" + fullDependencyName + "']");  
    5. var dependencyValue = params["dependencyvalue"];  
    6.  
    7. var acturalValue = null;  
    8. if(dependency.attr("type") == "checkbox"){  
    9. acturalValue = dependency.attr("checked") == true ? dependency.val() : null;  
    10. }else{  
    11. acturalValue = dependency.val();  
    12. }  
    13.  
    14. eval("dvs = " + dependencyValue);  
    15. if(dvs.indexOf2(acturalValue) >= 0){  
    16. return $.validator.methods.required.call(this, value, element, params);  
    17. }  
    18. return true;  
    19. }  
    20. Adapter  
    21. var requiredIfAdapter = function (options) {  
    22. options.rules["requiredIf"] = options.params;  
    23. options.messages["requiredIf"] = options.message;  

    注冊(cè):

     
     
     
     
    1. $.validator.addMethod('requiredIf',requiredIf);  
    2. $.validator.unobtrusive.adapters.add('requiredIf',['dependency', 'dependencyvalue'],requiredIfAdapter); 

    使用:

     
     
     
     
    1. …  
    2. [RequiredIf("CertificateType" , 2)]  
    3. public object Nation { get; set; }  
    4.  
    5. [RequiredIf("CertificateType" , 1 , 2)]  
    6. public object IDNumber { get; set; }  
    7. … 

    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 了

     
     
     
     
    1. public class MyRequiredAttributeAdapter : RequiredAttributeAdapter {  
    2. public MyRequiredAttributeAdapter(ModelMetadata metadata , ControllerContext context , RequiredAttribute attribute)  
    3. : base(metadata , context , attribute) {  
    4. //ErrorMessage 和 ErrorMessageResourceName 不可同時(shí)存在  
    5. if(attribute.ErrorMessage == null) {  
    6. //attribute.ErrorMessage = null;  
    7. attribute.ErrorMessageResourceType = typeof(CustomMessage);  
    8. attribute.ErrorMessageResourceName = "PropertyValueRequired";  
    9. }  
    10. }  

    其中,ErrorMessage 和 ErrorMessageResourceName 不能同時(shí)存在,這個(gè)需要注意。

    然后注冊(cè)這個(gè)適配器:

     
     
     
     
    1. Global  
    2.  
    3. 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

     
     
     
     
    1. protected override void OnActionExecuted(ActionExecutedContext filterContext) {  
    2.  
    3. if(TempData["ModelState"] != null && !ModelState.Equals(TempData["ModelState"]))  
    4.  
    5. ModelState.Merge((ModelStateDictionary)TempData["ModelState"]);  
    6.  
    7.  
    8. base.OnActionExecuted(filterContext);  
    9.  
    10. }  

    當(dāng)ModelState.IsValid 為 false 時(shí):

     
     
     
     
    1. if(this.ModelState.IsValid) {  
    2. ...  
    3. } else {  
    4. ...  
    5. this.TempData.Add("ModelState" , this.ModelState);  
    6. 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

     
     
     
     
    1. ///   
    2. /// 注冊(cè)腳本  
    3. ///   
    4. ///   
    5. ///   
    6. ///   
    7. public static MvcHtmlString Script(this HtmlHelper htmlHelper , Func template) {  
    8. htmlHelper.ViewContext.HttpContext.Items["_script_" + Guid.NewGuid()] = template;  
    9. return MvcHtmlString.Empty;  
    10. }  
    11. ///   
    12. /// 渲染腳本  
    13. ///   
    14. ///   
    15. ///   
    16. public static IHtmlString RenderScripts(this HtmlHelper htmlHelper) {  
    17. foreach(object key in htmlHelper.ViewContext.HttpContext.Items.Keys) {  
    18. if(key.ToString().StartsWith("_script_")) {  
    19. var template = htmlHelper.ViewContext.HttpContext.Items[key] as Func;  
    20. if(template != null) {  
    21. htmlHelper.ViewContext.Writer.Write(template(null));  
    22. }  
    23. }  
    24. }  
    25. return MvcHtmlString.Empty;  

    在 _Layout.cshtml 里:

     
     
     
     
    1. ....  
    2. @Html.RenderScripts()  
    3.  
    4.  

    在每個(gè) View 甚至 Partical View 里:

     
     
     
     
    1. @Html.Script(  
    2. @  

    這樣就保證了所有這樣寫的 script 代碼都在 body 結(jié)束的位置

    DisplayAttribute

    首先,我們的實(shí)體類的每個(gè)屬性都有指定 DisplayAttribute , 這樣,我們就可以用 Html.LabelFor 來(lái)統(tǒng)一屬性的顯示名稱。但是,你的 Display 定義的準(zhǔn)不準(zhǔn)呢?是不是客戶想要的文字呢?如果不是,你還要一個(gè)一個(gè)的去把 Display 給修改?會(huì)不會(huì)影響到其它人?

    為了做到最小影響,我加了一個(gè) DataAnnoationsModelMetaDataProvider

     
     
     
     
    1. public class DisplayNameMetadataProvider : DataAnnotationsModelMetadataProvider {  
    2.  
    3. private ResourceManager resourceManager = null;  
    4.  
    5. public DisplayNameMetadataProvider(ResourceManager manager) {  
    6. this.resourceManager = manager;  
    7. }  
    8. public override ModelMetadata GetMetadataForProperty(Func modelAccessor , Type containerType , string propertyName) {  
    9. var meadata = base.GetMetadataForProperty(modelAccessor , containerType , propertyName);  
    10.  
    11. var key = string.Format("{0}_{1}" , containerType.Name , propertyName);  
    12. var value = this.resourceManager.GetString(key);  
    13. if(value != null)  
    14. metadata.DisplayName = value;  
    15. return metadata;  
    16. }  
    17. }  
    18.  

       
       
       
       
      1. Globa  
      2. ModelMetadataProviders.Current = new DisplayNameMetadataProvider(OnlineUI.ResourceManager);  

      OnlineUI 是一個(gè)資源文件。這樣做的好處是:只有 OnlineUI 里有對(duì)應(yīng)的 ClassName_PropertyName 的時(shí)候,才會(huì)去對(duì)原有的 DisplayAttribute 去覆蓋,不存在的話,繼續(xù)沿用原有的。

      CheckBoxFor 的神秘隨從

      用 CheckBoxFor 的時(shí)候,你是否注意到它有一個(gè)你想不到的地方?它會(huì)生成一個(gè) hidden input, 它的name和 checkbox 的name 一樣。

      原因我也不清楚,給你一個(gè)參考:

      http://forums.asp.net/t/1314753.aspx

      AdditionalViewData 的困惑

      public static MvcHtmlString EditorFor(this HtmlHelper html , Expression> expression , string templateName , object additionalViewData);

      該方法的參數(shù) additionalViewData 的說(shuō)明:

      additionalViewData:

      An anonymous object that can contain additional view data that will be merged

      into the System.Web.Mvc.ViewDataDictionary instance that is created

      for the template.

      大意是說(shuō) additionalViewData 將會(huì)被合并到 ViewBag 內(nèi)。

      比如:Traveller.cshtml

       
       
       
       
      1. @Html.EditorFor(m => m[i].Nation , "Nation", new { Disabled = true }) 

      我聲明了一個(gè) additionalViewData :Disabled, 然后在 EditorTemplates 下的

      Nation.cshtml 里:

       
       
       
       
      1. @model string  
      2. @{  
      3. Layout = null;  
      4. var nation = Model == null ? "HK" : Model;  
      5. var required = (bool)(ViewBag.Required ?? false);  
      6. var disabled = (bool)(ViewBag.Disabled ?? false);  
      7. ...  
      8. ... 

      但是,不要太過(guò)于自信,將 additionalViewData 合并到 ViewBag 內(nèi),是有條件的:如果上面說(shuō)的 Traveller.cshtml 是通過(guò) Html.Action("Traveller",....) 調(diào)用的,這個(gè) additionalViewData 是死活也合并不進(jìn) ViewBag 內(nèi)的,除非用 Html.Partical("Traveller",....)

      至于為什么,我沒(méi)有去考證,只知道通過(guò) Html.Action 是死活都不行。

      Post 方法的參數(shù)是一個(gè)字典

      Post 方法如下:

       
       
       
       
      1. [HttpPost]  
      2.  
      3. public ActionResult Option([Bind(Include = "Date,Qty")]Dictionary opts) {  
      4.  
      5. ... 

      那要怎樣處理 html ,才能將Post 的值綁定到 opts 參數(shù)里?

      首先,要有一個(gè) key,

       
       
       
       
      1.  

      idx 必須從 0 開(kāi)始, 這個(gè)key 的 value 就是 dictionary 的 key

      字典的 value

       
       
       
       
      1. @Html.DropDownList(string.Format("opts[{0}].Value.Qty" , idx) , options.Select(o => new SelectListItem() { Text = o.ToString() , Value = o.ToString(), Selected = o == selectedQty })) 

      也就是說(shuō): 字典的 key 必須要有一個(gè) name 為 opts[0].Key 的html表單對(duì)象來(lái)存放

      字典的 value 的值必須要用這樣的 name 的 html 表單:opts[0].Value.Qty

      HtmlFieldPrefix

      暫未整理

      異常處理 ExceptionFilter

      異常處理程序的執(zhí)行順序

      關(guān)于順序,可以參考:

      http://msdn.microsoft.com/zh-cn/library/gg416513(v=vs.98).aspx

      OnActionExecuting(ActionExecutingContext)、OnResultExecuting(ResultExecutingContext) 和 OnAuthorization(AuthorizationContext) 篩選器以正向順序運(yùn)行。 OnActionExecuted(ActionExecutedContext)、OnResultExecuting(ResultExecutingContext) 和 OnException(ExceptionContext) 篩選器以反向順序運(yùn)行。

      也就是說(shuō)***注冊(cè)的 ExceptionFilter(不指定 Order 的情況下,認(rèn)為Order 是相同的) ***執(zhí)行,我在
      當(dāng)前文章:MVC3項(xiàng)目總結(jié)
      文章鏈接:http://www.dlmjj.cn/article/ccoihcc.html