ASP.NET MVC 2資料驗證

by Vivid 19. 四月 2011 23:12

在ASP.NET MVC模型中資料驗證發生錯誤時是利用一個名為Model State字典集合來表示錯誤資訊。此集合中包含多個Model State物件,每個物件代表特性屬性的狀態。您可以藉由傳遞一個Model State字典集合,將錯誤的資訊從Controller Action傳遞到View。在Controller與View都有一個叫做ModelState的屬性可存取到這些錯誤。本文將介紹如何在ASP.NET MVC 2專案中驗證資料。

為了方便說明,我建立一個ASP.NET MVC 2專案,然後在Model目錄中,新增一個ADO.NET Entity Data Model,命名為NW.edmx檔案,將Northwind資料庫中所有資料表加到模型之中。

筆者在HomeController類別階層建立了NorthwindEntities,此類別中有一個Create action透過ADO.NET Entity Framework用來新增Region資料。這個方法中,檢查RegionDescription的值長度不可以小於等於1,若驗證失敗,則叫用ModelStateDictionary的AddModelError方法,傳入key值(RegionDescription),與錯誤訊息(地區描述長度不可小於等於1),新增一個錯誤資訊到ModelStateDictionary:

NorthwindEntities db = new NorthwindEntities( );
public ActionResult Create( ) { return View( ); }
        [HttpPost]
        public ActionResult Create( Region r ) {
            if ( r.RegionDescription.Trim( ).Length <= 1 )
                ModelState.AddModelError( "RegionDescription" , "地區描述長度不可小於等於1" );
            if ( !ModelState.IsValid )
                return View( );
            db.Regions.AddObject( r );
            db.SaveChanges( );
            return RedirectToAction( "Index" );
        }

通常Key值都根據物件的屬性名稱來設定。只要有錯誤被加到ModelStateDictionary集合,則ModelState.IsValid屬性會回傳false,因此範例程式直接回傳View物件。若沒有發生錯誤,則將資料新增到資料庫,然後使用者會被導向Index View。

接著利用Visual Studio 2010工具為Create Action產生Create View,在Visual Studio程式碼視窗中,上列Create方法上方按滑鼠右鍵,選擇「Add View」,將View Name設定為「Create」,勾選「Create a strongly-typed view」,View data class則選取Model目錄中的Region。最後將View content設定為「Create」。

clip_image002

圖 1:建立Create View。

在Create View之中,您可以使用Html.ValidationSummary方法以及Html.ValidationMessageFor方法來顯示錯誤資訊,預設Visual Studio 2010會自動幫你產生程式碼叫用它們。

<%@ Page Title = "" Language = "C#" MasterPageFile = "~/Views/Shared/Site.Master" Inherits = "System.Web.Mvc.ViewPage< MvcApplication2.Models.Region>" % >
<asp:Content ID = "Content1" ContentPlaceHolderID = "TitleContent" runat = "server" >
    Create
</asp:Content>
<asp:Content ID = "Content2" ContentPlaceHolderID = "MainContent" runat = "server" >
<h2>Create</h2>
    <% using ( Html.BeginForm () ) { %>
        <%: Html.ValidationSummary(true) %>
        <fieldset>
            <legend> Fields </legend>
            <div class = "editor-label" >
                <%: Html.LabelFor( model => model.RegionID ) %>
            </div>
            <div class = "editor-field" >
                <%: Html.TextBoxFor(model => model.RegionID ) %>
                <%: Html.ValidationMessageFor( model => model.RegionID ) %>
            </div>
            <div class = "editor-label" >
                <%: Html.LabelFor( model => model.RegionDescription) % >
            </div>
            <div class = "editor-field" >
                <%: Html.TextBoxFor(model => model.RegionDescription ) %>
                <%: Html.ValidationMessageFor( model => model.RegionDescription ) %>
            </div>
            <p>
                <input type = "submit" value = "Create" />
            </p>
        </fieldset>
    <% } %>
    <div>
        <%: Html.ActionLink("Back to List", "Index") %>
    </div>
</asp:Content>


執行時,若驗證動作發生失敗,在網頁上方便會Html.ValidationSummary方法所在處自動顯示錯誤訊息,而每個物件的屬性則利用Html.ValidationMessageFor方法來顯示錯誤資訊,參考圖2所示:

clip_image004

圖 2:顯示驗證錯誤資訊。

Html.ValidationSummary方法與Html.ValidationMessageFor方法都是ASP.NET MVC內建的驗證輔助函式,Html.ValidationSummary方法會使用條列式清單列出ModelStateDictionary集合中所有的錯誤。而Html.ValidationMessageFor方法則是用來顯示一個特定的錯誤訊息,它的用途相同於Html.ValidationMessage方法,例如可以修改View改用Html.ValidationMessage也可以得到相同的效果:

<%@ Page Title = "" Language = "C#" MasterPageFile = "~/Views/Shared/Site.Master" Inherits = "System.Web.Mvc.ViewPage<MvcApplication2.Models.Region>" % >
<asp:Content ID = "Content1" ContentPlaceHolderID = "TitleContent" runat = "server" >
    Create
</asp:Content>
<asp:Content ID = "Content2" ContentPlaceHolderID = "MainContent" runat = "server" >
<h2>Create</h2>
    <% using ( Html.BeginForm () ) { %>
        <%: Html.ValidationSummary(true) %>

        <fieldset>
            <legend> Fields </legend>
            <div class = "editor-label" >
                <%: Html.LabelFor( model => model.RegionID ) %>
            </div>
            <div class = "editor-field" >
                <%: Html.TextBoxFor(model => model.RegionID ) %>
                <%:Html.ValidationMessage("RegionID") %>
            </div>
           
            <div class = "editor-label" >
                <%: Html.LabelFor( model => model.RegionDescription) % >
            </div>
            <div class = "editor-field" >
                <%: Html.TextBoxFor(model => model.RegionDescription ) %>
                <%:Html.ValidationMessage("RegionDescription ") %>
            </div>
           
            <p>
                <input type = "submit" value = "Create" />
            </p>
        </fieldset>
    <% } %>
    <div>
        <%: Html.ActionLink("Back to List", "Index") %>
    </div>
</asp:Content>

使用IDataErrorInfo介面驗證

IDataErrorInfo介面主要的功能是讓您能夠處理驗證過程中發生的例外錯誤。預設的Model Binder便是使用IDataErrorInfo介面來客製化驗證的錯誤訊息,利用屬性的索引子(Indexer)回傳一個字串,代表錯誤訊息。例如Controller保留基本程式碼,用來撰寫應用程式流程控制:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVC2_Validate.Models;
namespace MVC2_Validate.Controllers {
    [HandleError]
    public class HomeController : Controller {
        NorthwindEntities db = new NorthwindEntities();
        public ActionResult Index () {
            return View(db.Region.ToList());
        }
        public ActionResult Create () { return View(); }
        [HttpPost]
        public ActionResult Create ( Region r ) {
            if ( !ModelState.IsValid )
                return View();
            db.Region.AddObject(r);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        public ActionResult About () {
            return View();
        }
    }
}

在Models目錄中加入Region部分類別:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;

namespace MVC2_Validate.Models {
    public partial class Region : IDataErrorInfo {
        private Dictionary<string, string> _errs = new Dictionary<string, string>();
        partial void OnRegionDescriptionChanging( string value ) {
            if (value.Trim().Length <= 1)
                _errs.Add( "RegionDescription", "地區描述長度不可小於等於1" );
        }
        public string Error {
            get { return string.Empty; } //用來顯示物件階層的錯
        }
        public string this[ string columnName ] {
            get {
                if (_errs.ContainsKey( columnName ))
                    return _errs[ columnName ];
                else
                    return string.Empty;
            }
        }
    }
}

 

Region部分類別實作了IDataErrorInfo介面,此介面包含一個類別階層的Error屬性,以及一個索引子,用來存取Field階層的錯誤資訊。範例實作了Region部分類別的OnRegionDescriptionChanging方法,在這個方法中包含了資料驗證邏輯。當RegionDescription屬性的值變動時,會自動叫用OnRegionDescriptionChanging方法來進行驗證檢查,一旦驗證資料有問題,便將錯誤訊息新增到名為_errs的Dictionary<string , string>泛型集合之中。範例中要求RegionDescription的欄位值字元長度不可以小於或等於1。

分層設計

從一個設計良好的應用程式之角度來看,要讓應用程式能夠更容易維護與重複使用,應該將控制邏輯、資料存取邏輯、驗證程式碼分離,分層設計,參考圖3所示,而不該將所有東西寫在一起,如前述的範例所示。

clip_image006

圖 3:分層設計

以下的範例說明如何將Controller、Repository(資料存取層)與Service層(驗證程式碼)分開來設計。

分層設計-Repository

資料存取的程式碼是定義在RegionRepository類別之中,此類別實作IRegionRepository介面的RegionList與CreateRegion方法。在RegionRepository類別之中,利用Entity Framework來讀取、新增資料到資料庫。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MVC2_Validate.Models {
    public interface IRegionRepository {
        IEnumerable<Region> RegionList ( );
        void CreateRegion ( Region aRegion );
    }
    public class RegionRepository : IRegionRepository {
        NorthwindEntities db = new NorthwindEntities ( );
        public IEnumerable<Region> RegionList ( ) {
            return db.Region.ToList ( );
        }
        public void CreateRegion ( Region aRegion ) {
            db.Region.AddObject ( aRegion );
            db.SaveChanges ( );
        }
    }
}

分層設計-服務層

服務層用來撰寫驗證資料有效性的商邏輯,因此我們將資料驗證部分的程式碼,獨立到一個RegionService類別,此類別實作了IRegionService介面的RegionList與CreateRegion方法。RegionService實體建立時,可以利用建構函式初始化RegionRepository 物件實體。CreateRegion方法中加入了驗證資料有效性的程式碼,當驗證通過時,利用RegionRepository 來進行新增資料到資料庫的動作。RegionList方法本身也是透過RegionRepository來擷取資料:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MVC2_Validate.Models {
    public interface IRegionService {
        bool CreateRegion ( Region aRegion );
        IEnumerable<Region> RegionList ( );
    }
    public class RegionService : IRegionService {
        private ModelStateDictionary _msd;
        private IRegionRepository _rp;
        public RegionService ( ModelStateDictionary mState )
            : this ( mState , new RegionRepository ( ) ) {
        }
        public RegionService ( ModelStateDictionary modelState , IRegionRepository repository ) {
            _msd = modelState;
            _rp = repository;
        }
        public bool CreateRegion ( Region aRegion ) {
            if ( aRegion.RegionDescription.Trim ( ).Length <= 1 )
                _msd.AddModelError ( "RegionDescription" , "地區描述長度不可小於等於1" );
            if ( !_msd.IsValid )
                return false;
            _rp.CreateRegion ( aRegion );
            return true;
        }
        public IEnumerable<Region> RegionList ( ) {
            return _rp.RegionList ( );
        }
    }
}

分層設計-Controller

將資料驗證與存取的程式碼抽離之後,Controller的設計就變的更為簡單,在建構函式中初始化RegionService物件實體,而在Index與Create Action分別使用RegionService的方法進行處理:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVC2_Validate.Models;
namespace MVC2_Validate.Controllers {
    [HandleError]
    public class HomeController : Controller {
        private IRegionService _svc;
        public HomeController () {
            _svc = new RegionService( this.ModelState );
        }
        public HomeController ( IRegionService svc ) {
            _svc = svc;
        }
        public ActionResult Index() {
            return View(_svc.RegionList());
        }
        public ActionResult Create() { return View(); }
        [HttpPost]
        public ActionResult Create ( Region r ) {
            if ( _svc.CreateRegion(r) )
                return RedirectToAction("Index");
            return View();
        }
        public ActionResult About() {
            return View();
        }
    }
}

總結

在這篇文章中,簡單地介紹ASPNET MVC驗證的機制,利用ModelStateDictionary來存放驗證錯誤。您也可以利用IDataErrorInfo物件來實作驗證邏輯,以及設定錯誤訊息。IDataErrorInfo主要是讓您的程式能夠和ASP.NET MVC 1相容,為了讓應用程式易於重用,應該適當地進行分層設計。

Tags:

ASP.NET | 許薰尹Vivid Hsu | .NET Magazine國際中文電子雜誌

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List