// Validation logic
if (productToCreate.Name.Trim().Length == 0)
ModelState.AddModelError("Name", "Name is required.");
<%= Html.ValidationSummary() %>
在视图中,若有校验不通过,将会在调用 ValidateionSummary的地方显示文字信息。
指明被校验位置,:
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name") %>
<%= Html.ValidationMessage("Name", "*") %>
</p>
在调用 Validationessage的地方,会显示校验指示信息,第一个参数是要显示出错信息的ID,第二个参数是指示信息。
当一个校验被匹配时,形如以下
红色点号开头的四条信息就是调用ValidationSummary()的地方。
每个* 就是校验指示信息。
If you want to customize the appearance of these prebinding error messages then you need to create resource strings for these messages
如何创建资源字符串?
-----
校验不应在Controller中校验,那如何校验呢?在哪里校验吗?
在Model中实现IDataErrorInfo, 让Model binder自动校验:
这个接口,在System.ComponentModel命名空间中,从.net 1.0以来就有了,它的结构非常简单:
public interface IDataErrorInfo
{
string this[string columnName] { get; }
string Error { get; }
}
我们只需要实现一个字符串索引器和一个Error只读属性,即可。
举例:
using System.Collections.Generic;
using System.ComponentModel;
namespace MvcApplication1.Models
{
public partial class Movie : IDataErrorInfo
{
private Dictionary<string, string> _errors = new Dictionary<string, string>();
partial void OnTitleChanging(string value)
{
if (value.Trim().Length == 0)
_errors.Add("Title", "标题必填.");
}
partial void OnDirectorChanging(string value)
{
if (value.Trim().Length == 0)
_errors.Add("Director", "导演必填.");
}
#region IDataErrorInfo Members
public string Error
{
get
{
return string.Empty;
}
}
public string this[string columnName]
{
get
{
if (_errors.ContainsKey(columnName))
return _errors[columnName];
return string.Empty;
}
}
#endregion
}
}
然后在Controller中这样:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Movie movieToCreate)
{
// Validate
if (!ModelState.IsValid)
return View();
// Add to database
try
{
_db.AddToMovieSet(movieToCreate);
_db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
在IsValid时,Controller就会察看 IDataErrorInfo,确定校验能不能通过。
这样就把具体判断逻辑脱离Controller了.
但是这里,还有一个问题,我们的这个方案是依赖于ASP.NET MVC的验证机制的,我没办法把业务层移植到别的应用场景中(如WPF)中去,怎么办呢?
一种办法是创建一个Service层,Service层后面隐藏了我们所有的验证逻辑及业务逻辑,让Service层和Controller打交道,Service层再和业务层。
如何和ASP.NET MVC的验证机制沟通呢?反过来想,我可以为我们的业务层创建接口层,也可以为我们的验证机制创建接口层,把验证接口暴露出来。我们通过前面的代码可以知道,ASP.NET MVC的验证机制是放在ModelState中的,那么可以反客为主,利用Facade模式,创建一个包装类,把ModelState包起來,藏在后面,形成一个我们适合于我们的验证接口用的新外观即可。
用这种方式,如有新的变化出现的话,我们只要更改包装类即可。
具体如下:
业务层
1: using System.Collections.Generic;
2:
3: namespace MvcApplication1.Models
4: {
5: public class ProductService : IProductService
6: {
7: private IValidationDictionary _validatonDictionary;
8: private IProductRepository _repository;
9:
10: public ProductService(IValidationDictionary validationDictionary, IProductRepository repository)
11: {
12: _validatonDictionary = validationDictionary;
13: _repository = repository;
14: }
15:
16: protected bool ValidateProduct(Product productToValidate)
17: {
18: if (productToValidate.Name.Trim().Length == 0)
19: _validatonDictionary.AddError("Name", "Name is required.");
20: if (productToValidate.Description.Trim().Length == 0)
21: _validatonDictionary.AddError("Description", "Description is required.");
22: if (productToValidate.UnitsInStock < 0)
23: _validatonDictionary.AddError("UnitsInStock", "Units in stock cannot be less than zero.");
24: return _validatonDictionary.IsValid;
25: }
26:
27: public IEnumerable<Product> ListProducts()
28: {
29: return _repository.ListProducts();
30: }
31:
32: public bool CreateProduct(Product productToCreate)
33: {
34: // Validation logic
35: if (!ValidateProduct(productToCreate))
36: return false;
37:
38: // Database logic
39: try
40: {
41: _repository.CreateProduct(productToCreate);
42: }
43: catch
44: {
45: return false;
46: }
47: return true;
48: }
49: }
50:
51: public interface IProductService
52: {
53: bool CreateProduct(Product productToCreate);
54: System.Collections.Generic.IEnumerable<Product> ListProducts();
55: }
56: }
验证接口
1: namespace MvcApplication1.Models
2: {
3: public interface IValidationDictionary
4: {
5: void AddError(string key, string errorMessage);
6: bool IsValid { get; }
7: }
8: }
验证机制的包装器
1: using System.Web.Mvc;
2:
3: namespace MvcApplication1.Models
4: {
5: public class ModelStateWrapper : IValidationDictionary
6: {
7: private ModelStateDictionary _modelState;
8:
9: public ModelStateWrapper(ModelStateDictionary modelState)
10: {
11: _modelState = modelState;
12: }
13:
14:
15: #region IValidationDictionary Members
16:
17: public void AddError(string key, string errorMessage)
18: {
19: _modelState.AddModelError(key, errorMessage);
20: }
21:
22: public bool IsValid
23: {
24: get { return _modelState.IsValid; }
25: }
26: #endregion
27: }
28: }
最终的Controller
1: using System.Web.Mvc;
2: using MvcApplication1.Models;
3:
4: namespace MvcApplication1.Controllers
5: {
6: public class ProductController : Controller
7: {
8: private IProductService _service;
9:
10: public ProductController()
11: {
12: _service = new ProductService(new ModelStateWrapper(this.ModelState), new ProductRepository());
13: }
14:
15: public ProductController(IProductService service)
16: {
17: _service = service;
18: }
19:
20:
21: public ActionResult Index()
22: {
23: return View(_service.ListProducts());
24: }
25:
26:
27: //
28: // GET: /Product/Create
29:
30: public ActionResult Create()
31: {
32: return View();
33: }
34:
35: //
36: // POST: /Product/Create
37:
38: [AcceptVerbs(HttpVerbs.Post)]
39: public ActionResult Create([Bind(Exclude = "Id")] Product productToCreate)
40: {
41: if (!_service.CreateProduct(productToCreate))
42: return View();
43: return RedirectToAction("Index");
44: }
45: }
46: }
就这样,我们通过一层层解耦,把业务层和验证机制独立出来,使之可以广泛应用于其它各种场合。
还有一些其它的方法,有些验证方法是使用标签来验证的,甚至ASP.NET MVC团队就创建了一个验证框架,用标签来验证的,只是不提供官方的技术支持.地址在http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24471