AOP有的翻譯“面向切面編程”,有的是“面向方面編程”。其實名字不重要,思想才是核心,mvc的Filter讓我們很
方便達到這種面向方面編程,就是在現有代碼的基礎上注入外部代碼,也就是所謂的面向方面編程,比如身份
驗證。
下面通過一個具體的例子來體驗一下MVC的AOP。
1、定義一AuthenAdminAttribute特性類
public class AuthenAdminAttribute : FilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
//這個方法是在Action執行之前調用
var user = filterContext.HttpContext.Session["AdminUser"];
if (user == null)
{
//filterContext.HttpContext.Response.Redirect("/Account/Logon");
var Url = new UrlHelper(filterContext.RequestContext);
var url = Url.Action("Logon", "Account", new { area=""});
filterContext.Result = new RedirectResult(url);
}
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
//這個方法是在Action執行之后調用
}
}
是否登錄是通過Session鍵值為AdminUser的值是否為null判斷,如果為null也就是還沒有登錄或者過期了,就跳
轉到登錄頁面,即"/Account/Logon"。
注意:上面OnAuthentication是在最開始被調用的,也在ActionFilter方法之前。而OnAuthenticationChallenge
方法是在Action執行之后,返回視圖之前被調用。所以要把登錄驗證寫在方法OnAuthentication中。而且在里面
我們用到了設置filterContext.Result = new RedirectResult(url);而不是跳轉的形式,很關鍵的。MVC在執行
Filter時,如果我們手動用一個ActionResult對象指定了其Context對象的Result屬性的值,那么這個這個
ActionResult將作為這個請求的結果輸出,並且在這次請求管道中剩下的步驟將不再繼續執行。反之如果沒有
設置filterContext.Result的值,它會繼續執行接下來的步驟,甚至是Action方法,就算我們設置了跳轉。
2、定義登錄Controller
public class ManageController : Controller
{
public ActionResult Logon()
{
return View();
}
[HttpPost]
public ActionResult Logon(string username,string password)
{
if (username == "admin" && password == "admin")
{
Session["AdminUser"] = username;
return RedirectToAction("Index",
"Order", new { area = "Admin" });
}
else
ViewBag.Error = "用戶名或密碼不正確!";
return View();
}
public ActionResult LogOff()
{
if (Session["AdminUser"] != null)
{
Session["AdminUser"] = null;
}
return RedirectToAction("Logon");
}
}
Action名為Logon的方法就是里面就是登錄相關邏輯,這里的邏輯我為了簡單化,就直接把用戶名和密碼固定寫
死了。當然實際過程中你可以根據你自己的需要,比如結合數據庫表記錄來判斷用戶名密碼是否合法,如果合
法就相應的信息存在Session里面,當然你也可以存在Cookie里。
注意:這里登錄成功之后的存Session的key一定要和上面第一步的AuthenAdminAttribute用到的key一致。
3、設置需要登錄判斷的Controller或者Action
最后就是根據你的需要,看哪些Controller或者Action需要登錄才能訪問,只需要在前面加上我們在上面定義好
的特性類就可以了。
[AuthenAdmin]
public class CategoryController : Controller
{
public ActionResult Index()
{
//此處省略具體邏輯代碼
return View();
}
}
上面是對整個Controller都要登錄驗證,就是這個Controller的所有Action必須要登錄才能訪問。那如果要針對單
獨的一個Action控制,其它不做要求呢?這也很好辦。代碼這樣寫就可以了。
public class CategoryController : Controller
{
public ActionResult Index()
{
//此處省略具體邏輯代碼
return View();
}
[AuthenAdmin]
public ActinResult List()
{
//此處省略具體邏輯代碼
return View();
}
}
這樣就是只有List這個Action需要登錄,Index就不用登錄就能訪問。
最后,說一下第一步為什么我要自己實現IAuthenticationFilter接口,而不直接繼承AuthorizeAttribute,比如這
樣寫
public class CustomAuthAttribute : AuthorizeAttribute {
private bool localAllowed;
public CustomAuthAttribute(bool allowedParam) {
localAllowed = allowedParam;
}
protected override bool AuthorizeCore(HttpContextBase httpContext) {
var user = httpContext.Session["AdminUser"];
return user != null
}
}
可以看到AuthorizeAttribute核心方法是AuthorizeCore返回值是一個bool類型,也就是只能判斷是否登錄過了,
這種情況如果你的系統采用Form驗證這個,確實是一個簡單可行的方案。如果要涉及到更加細微的權限控制或
者要把登錄信息存儲到Session或者其實地方就不太好辦了,因為ASP.NET的Form驗證是基於Cookie的。所以
我一般建議自己實現接口IAuthenticationFilter,這樣讓我們的系統更加靈活,更加容易擴展和控制。
這樣最后來看我們的業務邏輯代碼不會有身份驗證相關的代碼,登錄驗證和業務邏輯代碼完全隔離開了,業務邏
輯代碼變得簡潔了許多,這就是我理解的:你只需要針對一方面編程,也就是AOP提倡的面向方面編程。AOP
技術讓我們的軟件模塊之間的耦合性降低,大大的提高的我們軟件的可維護性和可復用性,AOP你值得擁有。