初學ASP.NET MVC 學習筆記(八)-Action Filters

初學ASP.NET MVC 學習筆記(八)-Action Filters

 

Understanding Action Filters

最近在做一個填寫基本資料的View,在寫年齡的DrowDownList的時候,

因為求快,所以就直接在View上寫了一個下面的Code:

 

<% List<string> ageList = new List<string>();
       for (int i = 1; i <= 10;i++ )
       {
           ageList.Add(i.ToString());
       }
     %>
    <div>
    年齡:<%=Html.DropDownList("Age", new SelectList(ageList)) %>
    </div>

add26dc6b7c148a1a0912f0fb6b55b58

雖然出來的結果是成功的,但是把程式寫在View中,就違反了MVC的精神,也因此被念了一下。

後來我就把這段Code拿到Controller中去寫:

Controller

 

  public ActionResult Index()
        {
            List<string> ageList = new List<string>();
            for (int i = 1; i <= 10; i++)
            {
                ageList.Add(i.ToString());
            }
            ViewData["AgeSelectList"] = new SelectList(ageList);

            return View();
        }

View

 

 <%var age = ViewData["AgeSelectList"] as SelectList;%>

    <div>
    年齡:<%=Html.DropDownList("Age", age) %>
    </div>

出來的結果跟剛剛的一樣,View上也變得比較乾淨,但是呢,如果當Controller中有其他邏輯

譬如說寫入資料庫失敗的時候返回View,只要沒有run到這段Code,程式就會出錯

例如我隨便拋出一個Exception,然後直接返回View

 

public ActionResult Index()
        {
            try
            {
                throw new Exception();
                List<string> ageList = new List<string>();
                for (int i = 1; i <= 10; i++)
                {
                    ageList.Add(i.ToString());
                }
                ViewData["AgeSelectList"] = new SelectList(ageList);

                return View();
            }
            catch
            {
                return View();
            }
        }

d835b981cac14a239f5e3e7f2db5d9a0

就會出現上面的錯誤訊息。

所以一種方法就是在每次返回View前,都將SelectList的ViewData準備好,不過這樣實在太累也太笨

或是在剛進到Action時就把ViewData準備好,好像也不是什麼好方法…

 

 

所以我試了下面兩種方法:分別是用BaseController跟Action Filter

BaseController的方法就是先建一個中繼的Controller,讓他繼承Controller,

再讓每個自訂Controller繼承他,變成如下

 

public class BaseController:Controller
    {
        public BaseController()
        {
            List<string> ageList = new List<string>();
            for (int i = 1; i <= 10; i++)
            {
                ageList.Add(i.ToString());
            }
            ViewData["AgeSelectList"] = new SelectList(ageList);
        }
    }

 

public class HomeController : BaseController
    {
        public ActionResult Index()
        {
            try
            {
                throw new Exception();
                return View();
            }
            catch
            {
                return View();
            }
        }
    }

把我要的ViewData放在BaseController的建構子裡,只要Controller有繼承他,就會有ViewData可以用

雖然很方便,但是壞處就是不需要用到的Action也會經過BaseController,如果在BaseController中寫

太多Code對效能不太好,所以接下來改用Action Filter的方式解決‧

自訂一個Class,然後繼承ActionFilterAttribute,再override  OnActionExecuted 這個方法:

 

public class AgeFilter:ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            base.OnActionExecuted(filterContext);
            
            List<string> ageList = new List<string>();
            for (int i = 1; i <= 10; i++)
            {
                ageList.Add(i.ToString());
            }
            filterContext.Controller.ViewData["AgeSelectList"] = new SelectList(ageList);
        }
    }

之後只要在需要調用的Action上加一個Attribute

 

        [AgeFilter]   //<===加上自定的Filter Attribute
        public ActionResult Index()
        {
            try
            {
                throw new Exception();
                return View();
            }
            catch
            {
                return View();
            }
        }

就可以用了。

Action Filter好用的地方不只在這,例如在發生例外要把錯誤訊息Mail到信箱

或是在未登入的時候,要做某些事情,都可以用Action Filter來處理。