2014年10月4日 星期六

[iThome 第七屆鐵人賽 12] BaseController的重要性

在這一篇將會介紹爲什麽對框架來說有一個BaseController讓所有的Controller來繼承很重要,並且介紹一個簡單的強型別的RedirectToAction讓所有繼承這個BaseController的可以來使用。

透過這個強型別的RedirectToAction讓在寫Code的時候,不要犯下寫錯Action的名字,或者Action名字改了但是對應的RedirectToAction沒有改到的低級錯誤。

爲什麽要自定自己的BaseController

所有的Controller都會繼承System.Web.Mvc.Controller。繼承的概念在OO裡面很重要,因為可以讓一些所有Controller都通用的功能,透過繼承來傳下去。同時,一些特殊的Attribute如果放在Controller的層級,那麼所有的ActionResult都會有效果,因此針對我們的框架,就會定義一個框架的BaseController來讓所有的Controller繼承,以提供一些通用的方法。

強型別的RedirectToAction

定義一個BaseController非常的簡單,只需要定一個class然後讓他繼承System.Web.Mvc.Controller,之後我們實際要使用的Controller在繼承這個BaseController 就可以了。

 /// <summary>
/// Application 層級的BaseController
/// </summary>
public class BaseController : Controller
{
}

// 繼承BaseController
public class HomeController : BaseController
{
}

爲了未來的擴充性,因此就算目前沒有任何共通的方法而讓BaseController留空都沒關係,因為有了這一層,在未來需要添加內容就會變的相對簡單。


不過爲了不讓我們的BaseController什麽都沒有,我們來做一個很好用的功能,那就是加入強型別的RedirectToAction。


那什麽是強型別的RedirectToAction?基本上Controller已經有一個RedirectToAction的方法,不過這個方法和很多Mvc的helper方法一樣,接受一個string作為參數,而這個string就是所謂的“magic string”會幫我們對應到正確的Action。舉例來說,通常儲存成功都會做一個RedirectToAction("Index"),表示回傳的內容將會是這個Controller的Index ActionResult。


問題來了,假設今天打的太快,把“Index”打成“Indx”(或者是把Action從“Index”改成了“Home”但是忘記改對應的RedirectToAction),因為它是string,在compile time完全看不出來打錯了。只有到Runtime才會發現有問題。那這個地方就有可能被漏掉並且造成系統炸掉,如果剛好在客戶面前發生,那麼整個開發團隊感覺就low掉了。


image

少打一個字母,其實肉眼很不容易發現

而強型別的RedirectToAction就可以避免掉這個問題。


取得和使用強型別的RedirectToAction


有一個Nuget的package叫做:Microsoft.AspNet.Mvc.Futures


可以透過Package Mangement Console輸入:Install-Package Microsoft.AspNet.Mvc.Futures來安裝。


這個Package就有包含一個強型別的RedirectToAction,不過它屬於extension method,因此使用起來有些變扭:

// 繼承BaseController
public class HomeController : BaseController
{
public ActionResult Index()
{
return View();
}

public ActionResult Create()
{
...
return this.RedirectToAction<HomeController>(x => x.Index());
}
}

這個強型別的RedirectToAction是很好,可是用起來都需要加上this實在和原始的RedirectToAction感覺不一樣,難道沒有辦法讓呼叫強型別的時候,不要透過this?這時候我們的BaseController就能夠排上用場。


BaseController讓強型別的RedirectToAction用起來和原來的版本一樣


我們可在BaseController做一個Wrapper,把那個extension方法包起來,這樣呼叫起來感覺強型別的RedirectToAction就和原本的是一樣的呼叫方式:

/// <summary>
/// Application 層級的BaseController
/// </summary>
public class BaseController : Controller
{
/// <summary>
/// Redirects to action.
/// </summary>
/// <typeparam name="TController">The type of the controller.</typeparam>
/// <param name="action">The action.</param>
/// <returns></returns>
protected ActionResult RedirectToAction<TController>(Expression<Action<TController>> action)
where TController : Controller
{
return ControllerExtensions.RedirectToAction(this, action);
}
}

// 繼承BaseController
public class HomeController : BaseController
{
public ActionResult Index()
{
return View();
}

public ActionResult Create()
{
...
return RedirectToAction<HomeController>(x => x.Index());
}
}

這邊有一點需要注意,假設Index需要傳入參數,這個強型別的方法是會有問題。

結語


希望透過這個例子,能夠展示出有一個共通的Base是很好的一件事情,就算目前不需要擴充,但是只要所有的都繼承這個Base,以後要擴充就很方便。


同時希望透過介紹強型別的RedirectToAction讓在寫Code的時候,避免掉低級錯誤,像是打錯字,或者改名字沒有改到對應的“Magic String”


沒有留言 :

張貼留言