基于MVC+EasyUI的Web開發(fā)框架形成之旅--MVC控制器的設(shè)計(jì)

本文主要介紹整個(gè)Web開發(fā)框架中的MVC控制器的設(shè)計(jì)。在設(shè)計(jì)之初,我就希望盡可能的減少代碼,提高編程模型的統(tǒng)一性。因此希望能夠以基類繼承的方式,和我Winform開發(fā)框架一樣,盡可能通過基類,而不是子類的重復(fù)代碼來(lái)實(shí)現(xiàn)各種通用的操作。

1、登錄控制的控制器基類設(shè)計(jì)

我們知道,一般我們創(chuàng)建一個(gè)MVC的控制器,都是基于Controller這樣的基類來(lái)實(shí)現(xiàn)。如下代碼所示。


public class TestController : Controller
{
    // GET: /Test/

    public ActionResult Index()
    {
        return View();
    }

}

在我的Winform開發(fā)框架里面,用到了泛型的類型,非常方便實(shí)現(xiàn)業(yè)務(wù)邏輯和數(shù)據(jù)訪問基類的設(shè)計(jì),控制器是否也可以這樣做的呢?

我們知道,一般的MVC控制器需要驗(yàn)證用戶是否已經(jīng)登陸了,這也是很多常見Web操作前的驗(yàn)證,還有對(duì)異常的處理,在MVC的基類,可以一并進(jìn)行記錄(這個(gè)非常不錯(cuò)),于是我們先來(lái)設(shè)計(jì)一個(gè)驗(yàn)證用戶身份是否登陸的基類BaseController

/// <summary>
/// 所有需要進(jìn)行登錄控制的控制器基類
/// </summary>
public class BaseController : Controller 
{
    /// <summary>
    /// 當(dāng)前登錄的用戶屬性
    /// </summary>
    public UserInfo CurrentUserInfo { get; set; }

    /// <summary>
    /// 重新基類在Action執(zhí)行之前的事情
    /// </summary>
    /// <param name="filterContext">重寫方法的參數(shù)</param>
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);
        //得到用戶登錄的信息
        CurrentUserInfo = Session["UserInfo"] as UserInfo;

        //判斷用戶是否為空
        if (CurrentUserInfo == null)
        {
            Response.Redirect("/Login/Index");
        }
    }

    protected override void OnException(ExceptionContext filterContext)
    {
        base.OnException(filterContext);

        //錯(cuò)誤記錄
        WHC.Framework.Commons.LogTextHelper.Error(filterContext.Exception);

        // 當(dāng)自定義顯示錯(cuò)誤 mode = On,顯示友好錯(cuò)誤頁(yè)面
        if (filterContext.HttpContext.IsCustomErrorEnabled)
        {
            filterContext.ExceptionHandled = true;
            this.View("Error").ExecuteResult(this.ControllerContext);
        }
    }
........................
}

有了這個(gè)基類,我們?cè)谥黜?yè)的Home控制類,就可以使用用戶信息對(duì)象了進(jìn)行操作了,而且必須要求客戶登陸了。

public class HomeController : BaseController
{
    public ActionResult Index()
    {
        if (CurrentUserInfo != null)
        {
            ViewBag.FullName = CurrentUserInfo.FullName;
            ViewBag.Name = CurrentUserInfo.Name;
        }
        return View();
    }
................
}

2、數(shù)據(jù)訪問業(yè)務(wù)基類控制器的設(shè)計(jì)

我在我的Winform開發(fā)框架里面,對(duì)很多基類都使用泛型進(jìn)行設(shè)計(jì),這樣可以傳遞相應(yīng)的數(shù)據(jù)類型到基類里面進(jìn)行處理,如下面的BLL層的業(yè)務(wù)對(duì)象定義代碼如下所示。

namespace WHC.Security.BLL
{
    /// <summary>
    /// 角色信息業(yè)務(wù)管理類
    /// </summary>
    public class Role : BaseBLL<RoleInfo>
    {

....................
/// <summary>
/// 業(yè)務(wù)基類對(duì)象
/// </summary>
/// <typeparam name="T">業(yè)務(wù)對(duì)象類型</typeparam>
public class BaseBLL<T> where T : BaseEntity, new()
{

    /// <summary>
    /// 插入指定對(duì)象到數(shù)據(jù)庫(kù)中
    /// </summary>
    /// <param name="obj">指定的對(duì)象</param>
    /// <returns>執(zhí)行操作是否成功。</returns>
    public virtual bool Insert(T obj)
    {
        return baseDal.Insert(obj);
    }

............

業(yè)務(wù)對(duì)象Role,要求傳入RoleInfo給基類處理,這樣基類就能定義到都對(duì)應(yīng)的T為具體的RoleInfo類型了。在MVC的控制器是否也可以這樣做呢?當(dāng)然可以,下面是我定義的一個(gè)控制器繼承關(guān)系圖。



上面的介紹也已經(jīng)比較明白了,其實(shí)就是在BusinessController<B, T>里面?zhèn)魅肓藘蓚€(gè)參數(shù),定義代碼如下所示。

/// <summary>
/// 本控制器基類專門為訪問數(shù)據(jù)業(yè)務(wù)對(duì)象而設(shè)的基類
/// </summary>
/// <typeparam name="B">業(yè)務(wù)對(duì)象類型</typeparam>
/// <typeparam name="T">實(shí)體類類型</typeparam>
public class BusinessController<B, T> : BaseController
    where B : class
    where T : WHC.Framework.ControlUtil.BaseEntity, new()
{

    /// <summary>
    /// 插入指定對(duì)象到數(shù)據(jù)庫(kù)中
    /// </summary>
    /// <param name="info">指定的對(duì)象</param>
    /// <returns>執(zhí)行操作是否成功。</returns>
    public virtual ActionResult Insert(T info)
    {
        bool result = false;
        if (info != null)
        {
            result = baseBLL.Insert(info);
        }
        return Content(result);
    }

................

我根據(jù)傳入的BLL業(yè)務(wù)對(duì)象類型B,對(duì)象實(shí)體類類型T,那么我們就可以構(gòu)造對(duì)應(yīng)的baseBLL對(duì)象,然后調(diào)用其基類接口實(shí)現(xiàn)基本的操作,如插入,刪除,更新,查找等等,這樣的模式就和我的Winform開發(fā)框架的理念非常吻合了。

我們以角色控制器來(lái)說明,它的定義如下所示,如果不需要實(shí)現(xiàn)額外的接口(除了常見的操作),基本上不需要寫任何代碼了,因?yàn)樗泻芏喑R姷牟僮?,都已?jīng)封裝在了基類控制器BusinessController<B, T>里面了。

/// <summary>
/// 角色業(yè)務(wù)操作控制器
/// </summary>
public class RoleController : BusinessController<Role, RoleInfo>
{
    public RoleController() : base()
    {
    }

...............

對(duì)于一些需要特殊數(shù)據(jù)處理的操作,可以增加一些自定義的接口函數(shù),也可以重寫基類的一些接口,實(shí)現(xiàn)數(shù)據(jù)的相應(yīng)處理。如我的菜單界面顯示中,需要根據(jù)縮進(jìn)的層級(jí)對(duì)菜單名稱進(jìn)行縮進(jìn),以便更好的展示它們的層級(jí)結(jié)構(gòu),那么我就需要對(duì)分頁(yè)函數(shù)進(jìn)行重寫了,如下代碼所示是整個(gè)菜單Menu類的控制器類代碼。

public class MenuController : BusinessController<Menu, MenuInfo>
{
    public override ActionResult FindWithPager()
    {
        string where = GetPagerCondition(); //基類實(shí)現(xiàn)
        PagerInfo pagerInfo = GetPagerInfo(); //基類實(shí)現(xiàn)            
        List<MenuInfo> list = baseBLL.FindWithPager(where, pagerInfo);
        list = CollectionHelper<MenuInfo>.Fill("-1", 0, list, "PID", "ID", "Name");

        //Json格式的要求{total:22,rows:{}}
        //構(gòu)造成Json的格式傳遞
        var result = new { total = pagerInfo.RecordCount, rows = list };
        return JsonDate(result);
    }

    /// <summary>
    /// 用作下拉列表的菜單Json數(shù)據(jù)
    /// </summary>
    /// <returns></returns>
    public ActionResult GetDictJson()
    {
        List<MenuInfo> list = baseBLL.GetAll();
        list = CollectionHelper<MenuInfo>.Fill("-1", 0, list, "PID", "ID", "Name");

        List<CListItem> itemList = new List<CListItem>();
        foreach (MenuInfo info in list)
        {
            itemList.Add(new CListItem(info.Name, info.ID));
        }
        return Json(itemList, JsonRequestBehavior.AllowGet);
    }
}

我們來(lái)看看一段HTML頁(yè)面里面,使用javascript腳本調(diào)用控制器API來(lái)實(shí)現(xiàn)數(shù)據(jù)的綁定的操作,也就是使用示例。

$.getJSON("/Role/FindById?r=" + Math.random() + "&id=" + id, function (json) {
    $("#txtID").val(json.ID);
    $("#txtName").val(json.Name);
    $("#txtNote").val(json.Note);
});

上面這個(gè)很標(biāo)準(zhǔn)的接口FindById是業(yè)務(wù)基類控制器BusinessController<B, T>里提供的。
當(dāng)然,BusinessController里面可以類似我Winform開發(fā)框架里面基類一樣,提供很豐富的操作接口,如返回列表Json集合,增刪改查的操作及返回,分頁(yè)數(shù)據(jù)的返回,以及一些特殊的操作都可以實(shí)現(xiàn)。而這些都不需要子類進(jìn)行任何實(shí)現(xiàn)。
如下面實(shí)際案例的用戶登陸日志,里面的界面功能還是很豐富的,當(dāng)他的控制器業(yè)務(wù)類不需要任何實(shí)現(xiàn),只需要繼承基類即可。


public class LoginLogController : BusinessController<LoginLog, LoginLogInfo>
{
    public LoginLogController() : base()
    {
    }

}

界面部分代碼如下所示。

//實(shí)現(xiàn)對(duì)DataGird控件的綁定操作
function InitGrid(queryData) {
    $('#grid').datagrid({   //定位到Table標(biāo)簽,Table標(biāo)簽的ID是grid
        url: '/LoginLog/FindWithPager',   //指向后臺(tái)的Action來(lái)獲取當(dāng)前用戶的信息的Json格式的數(shù)據(jù)
        title: '用戶登陸日志', 
        //下面的這些屬性如果誰(shuí)不太清楚的話我建議去官方網(wǎng)站去學(xué)習(xí)
        iconCls: 'icon-view',
        height: 450,
        nowrap: true,
        autoRowHeight: false,
        striped: true,
        collapsible: true,
        pagination: true,
        rownumbers: true,
        //sortName: 'ID',    //根據(jù)某個(gè)字段給easyUI排序
        sortOrder: 'asc',
        remoteSort: false,
        idField: 'ID',
        queryParams: queryData,  //異步查詢的參數(shù)
        columns: [[
            { field: 'ck', checkbox: true },   //選擇
            { title: 'ID', field: 'ID', width: 40, sortable: true },  //主鍵
             { title: '登錄用戶ID', field: 'User_ID', width: 80, sortable: true },
             { title: '登錄名稱', field: 'LoginName', width: 80, sortable: true },
             { title: '真實(shí)名稱', field: 'FullName', width: 80, sortable: true },
             { title: '日志描述', field: 'Note', width: 100, sortable: true },
             { title: 'IP地址', field: 'IPAddress', width: 100, sortable: true },
             { title: 'Mac地址', field: 'MacAddress', width: 120, sortable: true },
             { title: '系統(tǒng)編號(hào)', field: 'SystemType_ID', width: 120, sortable: true },
             { title: '記錄日期', field: 'LastUpdated', width: 120, sortable: true },
        ]],
        toolbar: [{
            id: 'btnAdd',
            text: '添加',
            iconCls: 'icon-add',
            handler: function () {                        
                ShowAddDialog();//實(shí)現(xiàn)添加記錄的頁(yè)面
            }
        }, '-', {
            id: 'btnEdit',
            text: '修改',
            iconCls: 'icon-edit',
            handler: function () {                        
                ShowEditOrViewDialog();//實(shí)現(xiàn)修改記錄的方法
            }
        }, '-', {
            id: 'btnDelete',
            text: '刪除',
            iconCls: 'icon-remove',
            handler: function () {                        
                Delete();//實(shí)現(xiàn)直接刪除數(shù)據(jù)的方法
            }
        }, '-', {
            id: 'btnView',
            text: '查看',
            iconCls: 'icon-table',
            handler: function () {                        
                ShowEditOrViewDialog("view");//實(shí)現(xiàn)查看記錄詳細(xì)信息的方法
            }
        }, '-', {
            id: 'btnReload',
            text: '刷新',
            iconCls: 'icon-reload',
            handler: function () {
                //實(shí)現(xiàn)刷新欄目中的數(shù)據(jù)
                $("#grid").datagrid("reload");
            }
        }]
    });

    $('#grid').datagrid({
        onDblClickRow: function (rowIndex, rowData) {
            $('#grid').datagrid('uncheckAll');
            $('#grid').datagrid('checkRow', rowIndex);
            ShowEditOrViewDialog();
        }
    });
}

這個(gè)就是我的控制器設(shè)計(jì)的中心思想了,下一篇繼續(xù)介紹整體的MVC系列的Web開發(fā)框架,介紹其中Web界面部分的處理和相關(guān)經(jīng)驗(yàn),希望大家多多提出寶貴的意見。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容