基于MVC+EasyUI的Web开发框架形成之旅–MVC控制器的设计
admin
2023-08-02 16:07:07
0

本文主要介绍整个Web开发框架中的MVC控制器的设计。在设计之初,我就希望尽可能的减少代码,提高编程模型的统一性。因此希望能够以基类继承的方式,和我Winform开发框架一样,尽可能通过基类,而不是子类的重复代码来实现各种通用的操作。

1、登录控制的控制器基类设计

我们知道,一般我们创建一个MVC的控制器,都是基于Controller这样的基类来实现。如下代码所示。

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

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

}

在我的Winform开发框架里面,用到了泛型的类型,非常方便实现业务逻辑和数据访问基类的设计,控制器是否也可以这样做的呢?

我们知道,一般的MVC控制器需要验证用户是否已经登陆了,这也是很多常见Web操作前的验证,还有对异常的处理,在MVC的基类,可以一并进行记录(这个非常不错),于是我们先来设计一个验证用户身份是否登陆的基类BaseController

/// 
/// 所有需要进行登录控制的控制器基类
/// 
public class BaseController : Controller 
{
    /// 
    /// 当前登录的用户属性
    /// 
    public UserInfo CurrentUserInfo { get; set; }

    /// 
    /// 重新基类在Action执行之前的事情
    /// 
    /// 重写方法的参数
    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);

        //错误记录
        WHC.Framework.Commons.LogTextHelper.Error(filterContext.Exception);

        // 当自定义显示错误 mode = On,显示友好错误页面
        if (filterContext.HttpContext.IsCustomErrorEnabled)
        {
            filterContext.ExceptionHandled = true;
            this.View(\"Error\").ExecuteResult(this.ControllerContext);
        }
    }
........................
}

有了这个基类,我们在主页的Home控制类,就可以使用用户信息对象了进行操作了,而且必须要求客户登陆了。

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

2、数据访问业务基类控制器的设计

我在我的Winform开发框架里面,对很多基类都使用泛型进行设计,这样可以传递相应的数据类型到基类里面进行处理,如下面的BLL层的业务对象定义代码如下所示。

namespace WHC.Security.BLL
{
    /// 
    /// 角色信息业务管理类
    /// 
    public class Role : BaseBLL
    {

....................
/// 
/// 业务基类对象
/// 
/// 业务对象类型
public class BaseBLL where T : BaseEntity, new()
{

    /// 
    /// 插入指定对象到数据库中
    /// 
    /// 指定的对象
    /// 执行操作是否成功。
    public virtual bool Insert(T obj)
    {
        return baseDal.Insert(obj);
    }

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

业务对象Role,要求传入RoleInfo给基类处理,这样基类就能定义到都对应的T为具体的RoleInfo类型了。在MVC的控制器是否也可以这样做呢?当然可以,下面是我定义的一个控制器继承关系图。

上面的介绍也已经比较明白了,其实就是在BusinessController里面传入了两个参数,定义代码如下所示。

/// 
/// 本控制器基类专门为访问数据业务对象而设的基类
/// 
/// 业务对象类型
/// 实体类类型
public class BusinessController : BaseController
    where B : class
    where T : WHC.Framework.ControlUtil.BaseEntity, new()
{

    /// 
    /// 插入指定对象到数据库中
    /// 
    /// 指定的对象
    /// 执行操作是否成功。
    public virtual ActionResult Insert(T info)
    {
        bool result = false;
        if (info != null)
        {
            result = baseBLL.Insert(info);
        }
        return Content(result);
    }

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

我根据传入的BLL业务对象类型B,对象实体类类型T,那么我们就可以构造对应的baseBLL对象,然后调用其基类接口实现基本的操作,如插入,删除,更新,查找等等,这样的模式就和我的Winform开发框架的理念非常吻合了。

我们以角色控制器来说明,它的定义如下所示,如果不需要实现额外的接口(除了常见的操作),基本上不需要写任何代码了,因为所有很多常见的操作,都已经封装在了基类控制器BusinessController里面了。

/// 
/// 角色业务操作控制器
/// 
public class RoleController : BusinessController
{
    public RoleController() : base()
    {
    }

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

对于一些需要特殊数据处理的操作,可以增加一些自定义的接口函数,也可以重写基类的一些接口,实现数据的相应处理。如我的菜单界面显示中,需要根据缩进的层级对菜单名称进行缩进,以便更好的展示它们的层级结构,那么我就需要对分页函数进行重写了,如下代码所示是整个菜单Menu类的控制器类代码。

public class MenuController : BusinessController
{
    public override ActionResult FindWithPager()
    {
        string where = GetPagerCondition(); //基类实现
        PagerInfo pagerInfo = GetPagerInfo(); //基类实现            
        List list = baseBLL.FindWithPager(where, pagerInfo);
        list = CollectionHelper.Fill(\"-1\", 0, list, \"PID\", \"ID\", \"Name\");

        //Json格式的要求{total:22,rows:{}}
        //构造成Json的格式传递
        var result = new { total = pagerInfo.RecordCount, rows = list };
        return JsonDate(result);
    }

    /// 
    /// 用作下拉列表的菜单Json数据
    /// 
    /// 
    public ActionResult GetDictJson()
    {
        List list = baseBLL.GetAll();
        list = CollectionHelper.Fill(\"-1\", 0, list, \"PID\", \"ID\", \"Name\");

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

我们来看看一段HTML页面里面,使用javascript脚本调用控制器API来实现数据的绑定的操作,也就是使用示例。

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

上面这个很标准的接口FindById是业务基类控制器BusinessController里提供的。
当然,BusinessController里面可以类似我Winform开发框架里面基类一样,提供很丰富的操作接口,如返回列表Json集合,增删改查的操作及返回,分页数据的返回,以及一些特殊的操作都可以实现。而这些都不需要子类进行任何实现。
如下面实际案例的用户登陆日志,里面的界面功能还是很丰富的,当他的控制器业务类不需要任何实现,只需要继承基类即可。

public class LoginLogController : BusinessController
{
    public LoginLogController() : base()
    {
    }

}

界面部分代码如下所示。

//实现对DataGird控件的绑定操作
function InitGrid(queryData) {
    $(\'#grid\').datagrid({   //定位到Table标签,Table标签的ID是grid
        url: \'/LoginLog/FindWithPager\',   //指向后台的Action来获取当前用户的信息的Json格式的数据
        title: \'用户登陆日志\', 
        //下面的这些属性如果谁不太清楚的话我建议去官方网站去学习
        iconCls: \'icon-view\',
        height: 450,
        nowrap: true,
        autoRowHeight: false,
        striped: true,
        collapsible: true,
        pagination: true,
        rownumbers: true,
        //sortName: \'ID\',    //根据某个字段给easyUI排序
        sortOrder: \'asc\',
        remoteSort: false,
        idField: \'ID\',
        queryParams: queryData,  //异步查询的参数
        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: \'真实名称\', 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: \'系统编号\', field: \'SystemType_ID\', width: 120, sortable: true },
             { title: \'记录日期\', field: \'LastUpdated\', width: 120, sortable: true },
        ]],
        toolbar: [{
            id: \'btnAdd\',
            text: \'添加\',
            iconCls: \'icon-add\',
            handler: function () {                        
                ShowAddDialog();//实现添加记录的页面
            }
        }, \'-\', {
            id: \'btnEdit\',
            text: \'修改\',
            iconCls: \'icon-edit\',
            handler: function () {                        
                ShowEditOrViewDialog();//实现修改记录的方法
            }
        }, \'-\', {
            id: \'btnDelete\',
            text: \'删除\',
            iconCls: \'icon-remove\',
            handler: function () {                        
                Delete();//实现直接删除数据的方法
            }
        }, \'-\', {
            id: \'btnView\',
            text: \'查看\',
            iconCls: \'icon-table\',
            handler: function () {                        
                ShowEditOrViewDialog(\"view\");//实现查看记录详细信息的方法
            }
        }, \'-\', {
            id: \'btnReload\',
            text: \'刷新\',
            iconCls: \'icon-reload\',
            handler: function () {
                //实现刷新栏目中的数据
                $(\"#grid\").datagrid(\"reload\");
            }
        }]
    });

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

这个就是我的控制器设计的中心思想了,下一篇继续介绍整体的MVC系列的Web开发框架,介绍其中Web界面部分的处理和相关经验,希望大家多多提出宝贵的意见。

相关内容

热门资讯

500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
python查找阿姆斯特朗数 题目解释 如果一个n位正整数等于其各位数字的n次方之和,则称该数为阿姆斯特朗数。 例如1^3 + 5...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...