2015年9月26日 星期六

ASP.NET 表單驗證

在看了Demo於youtube的twMVC頻道發表的Asp.Net表單驗證後,順手將影片中一些重點註記並以自己的觀點作了一些筆記, 以利後續使用表單驗證時可以參考使用。

影片來源: ASP.NET MVC內建驗證活用與擴充 [2012-07-14] –twMVC

Source Code: https://github.com/twMVC/twMVC-3-2

Demo的MVC驗證系列文章: http://demo.tc/series/asp_net%20mvc3%20%E9%A9%97%E8%AD%89%E4%BB%8B%E7%B4%B9%E5%AF%A6%E4%BD%9C%E8%88%87%E6%93%B4%E5%85%85


重點整理

驗證使用機制

  • 使用jQuery.Validate作前端驗證, 一次的工就可以實作前後端驗證
  • 繼承IClientValidatable, IValidatableObject, 擴充方便
  • Unobtrusive (非侵入式): 在後端將驗證內容附掛進HTML的attribute

驗證種類

  • Required
  • Compare
  • Remote: 真後端假前端自定驗證
  • StrengLength
  • RegularExpression
  • AllowHtml: 允許單一欄位輸入指令, 不太算內建的一部份,但很好用.
  • Url
  • EmailAddress
  • Range


內定驗證範例筆記

Required

不可空白

語法

[Required(ErrorMessage = "就說了必填")]
[Required]
public string Name;

重點

  • 對應到form時若有加Required屬性的欄位沒有填就會顯示錯誤訊息
  • 沒有填Error Message的屬性, 若驗證錯誤會顯示預設的錯誤訊息

StringLength

用來比對2個欄位是否相同. 可設定最大字數, 最小字數。

語法

StringLength(3)
StringLength(3, MinimumLength=3)

Compare

用來比對2個欄位是否相同.

語法/用法

public string Password { get; set; }
[Compare("Password", ErrorMessage = "密碼不一致")]
public string ConfirmPassword { get; set; }

RegularExpression

  • 超強, 超難用.
  • HTML會洩漏Pattern
  • 有安全上的隱憂
  • 寫不好會有效能上的問題
  • 微軟提供免費REGEX的檢測工具: SDL Regex Fuzzer, 可檢測好不好, 有沒有危險

Remote

* 利用Ajax在後端驗證 - 每打一個字到欄位中就會去驗證一次
* 有Area的地雷
* 驗證專用的controller的該方法一定要加上No Cache, 因為它是用GET, 會被browser做cache
* 該controller都是no cache

public JsonResult CheckUserName(string userName)
{
bool isValidate = userName != "demoshop";
// Remote 驗證是使用 Get 因此要開放
return Json(isValidate, JsonRequestBehavior.AllowGet);
}

AllowHtml


可個別允許單一欄位輸入HTML(指令)


優缺點



  • 不必修改<httpRun time RequestValidationMode=”2.0” />
  • 比[ValidateInput(false)]安全
  • 缺點是不得使用FormCollection

語法

public class TestModelsAllowHtml
{
[Display(Name = "AllowHtml")]
[AllowHtml]
public string AllowHtml { get; set; }
}

實作測試


在輸入欄位中加入<hr/>的HTML碼, submit後會回饋錯誤訊息: 具有潛在危險 Request.Form 的值已從用戶端 (AllowHtml="123 <hr/>") 偵測到。只要加上AllowHtml即可允許單一欄位可接受HTML.





自定驗證範例筆記


基於jQuery.Validate, 自定驗證有四大類



  • Bool
  • SingleVal
  • MinMax
  • Method

SingleValue範例


慣例是建立一個Attribute資料夾, 將自定驗證的程式碼寫在裡面.

//注意! 類別命名一定要是Attribute結尾
sealed public class NoIsAttribute : ValidationAttribute
{
public string Input { get; set; }

//範例需求: 需要有一個傳入值,以判斷傳入值是否與我們要比較的字串相等.
public NoIsAttribute(string input)
{
Input = input;
}
public override bool IsValid(object value)
{
if (value.Equals(null))
return true;

//輸入值與欄位值相同就報錯
if (value is string)
{
return !Input.Equals(value.ToString());
}

return true;
}
}
//加入NoIs屬性, 一定要帶一個字串參數(因為我們設計的NoIs一定要有一個參數)
public class CustomTestModels
{
[Display(Name = "Name (不能輸入 mvc)")]
[NoIs("mvc")]
public string Name { get; set; }

[Display(Name = "Email 驗證")]
public string Email { get; set; }
}

然後去實作controller和View後, 發現只會做後端驗證, 原來是沒有繼承IClientValidatable。所以要再繼承並實作介面GetClientValidationRules()

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule rule = new ModelClientValidationRule
{
ValidationType = "nois",
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())
};
//此參數一定要小寫
rule.ValidationParameters["input"] = Input;
yield return rule;
}

執行後發現還是沒有做前端驗證。到底問題出在哪? 因為前端的Javascript並沒有加入自定的參數到jQuery.Validate中。

<script type="text/javascript">
$.validator.addMethod("nois", function (value, element, param) {
if (value === false) {
return true;
}
if (value.indexOf(param) != -1) {
return false;
}
else {
return true;
}
});
$.validator.unobtrusive.adapters.addSingleVal("nois", "input");
</script>

上面的JS程式碼必需放在@section Scripts中, 才會在jQuery.js被載入後被執行. 再驗證一次就可成功執行前端驗證了.


統整一下自定驗證的注意事項:



  • 需將此類別宣告為sealed
  • 類別名稱必需是Attribute結尾
  • 需要繼承ValidationAttribute, IClientValidatable(如需前端驗證)
  • 必需override IsValid方法
  • GetClientValidationRules的ValidationType, ValidationParameters一定要小寫

驗證技巧


統一擴充於單一JS: 建立一個jQuery.Validate.zplugin.js (這個檔案一定要在jquery.validate.unobtrusive.js之後被載入, 否則會沒有用)。這樣就可以將所有自訂驗證的JS放在同一個檔案中統一管理.


不透過Submit呼叫驗證方法: jQuery.Validate是透過Submit handler去觸發的. 假如表單只用ajax往後傳(只用button, 而不用type=submit), 而不透過submit時就會需要這個方式.

<script type="text/javascript">
$(function() {
$(":button").click(function (e) {
e.preventDefault();
$('form').valid();
});
});
</script>

關掉單一欄位的前端驗證


可以在view中直接對該欄位的一個屬性設定為false關掉前端驗證.

@Html.TextBoxFor(m => m.Name, new { @class = "form-control", @data_val = false })

可以對應的POST action中對該欄位的一個屬性移除關掉前端驗證.

[HttpPost]
public ActionResult Index(CustomTestModels m)
{
ModelState.Remove("Name");
return View();
}

自訂Model驗證


如果使用EF, 可抽取出MetaData的Partial class然後繼承IValidatableObject後, 實作public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)來達到Model驗證作為最後的防線. 因為沒有找到範例程式, 以影片中的snapshot作參考, 日後有空可以再Study.


image


總結


所有Metadata可以從T4 Template來Generate code. 這樣就可以不用寫code來作表單驗證. --> 不太懂這句話, 有空再研究.






其他


使用Resource可以做到多國語言顯示錯誤訊息



  • 1. 新增App_GlobalResources folder
  • 2. 在該folder下新增Resource1, Resource1.en-US
  • 3. 在Resource1.resx下新增中文字對應表
  • 4. 在Resource1.en-US.resx下新增英文字對應表
    -->但目前在VS2015中, 加入了resx, 但在class中加入對應的字還是會編譯錯誤.

使用Remote將Controller或method設No Cache


參考http://stackoverflow.com/questions/12948156/asp-net-mvc-how-to-disable-automatic-caching-option中的方法:

//for controller
[OutputCacheAttribute(VaryByParam = "*", Duration = 0, NoStore = true)] // will be applied to all actions in MyController, unless those actions override with their own decoration
public class MyController : Controller
{
// ...
}

//for method in controller
[OutputCacheAttribute(VaryByParam = "*", Duration = 0, NoStore = true)] // will disable caching for CheckUserName only
public ActionResult Index()

參考資源


沒有留言:

張貼留言