本次主要分享几个场景的处理代码,有更好处理方式多多交流,相互促进进步;代码由来主要是这几天使用前端Ace框架做后台管理系统,这Ace是H5框架里面的控件效果挺多的,做兼容也很好,有点遗憾是控件效果基本都是写一起的,分离起来挺麻烦的;这次主要说的是后端代码,以后可以分享下这个框架的使用。

以上是个人的看法,下面来正式分享今天的文章吧:

. 扩展HtmlHelper,枚举转化select下拉框效果

. 自定义ActionFilter,验证登陆和权限访问

. 扩展HtmlHelper,无限递归生成菜单栏

. Global中增加全局Application_Error监控404异常

. 实现及使用缓存工厂(最新缓存工厂代码在上一篇分享的缓存工厂之Redis缓存)

下面一步一个脚印的来分享:

. 扩展HtmlHelper,枚举转化select下拉框效果

枚举转化select下拉框效果,这个效果估计很多同学都遇到过,也一定有自己的想法与实践;因为这里是MVC框架,所以这里我直接扩展HtmlHelper,这样在页面使用起来也很方便;这里了解一下这样的场景,通常枚举在方法中传递都只能是定义好的某一个枚举,这样来生成select标签扩展性就不强;这个时候有朋友就想到如果使用enum来当做方法的参数呢,这样是不行的,enum不能直接用来当做方法参数(看官们可以试试),所以这样看就没法定义一个公共的参数来传递不同枚举了,当然万能的Type给了我们一点曙光,下面我们就使用Type当做参数来传递枚举;

首先,我们既然要扩展HtmlHelper,那必须遵循一定的规则:

1.定义扩展类的类名通常使用Extension结尾,这里咋们定义个名称为HtmlHelperExtension的扩展类

2.扩展方法参数中使用this HtmlHelper html作为第一个参数

3.扩展方法返回MvcHtmlString把内容输出到试图View中

再来,自定义方法如:public static MvcHtmlString DrpDownByEnum(this HtmlHelper html, Type ty, string name = "Status", bool isAll = true);第二个参数就是上面说到的Type,她针对说有类型不仅仅局限于枚举,因为这里说的是使用枚举所以这里的职责就是负责枚举类型的传入,第三个参数是每个html标签都应该具备的name属性值,第四个参数是是否增加全选的选项;下面再来分享下具体代码:

 /// <summary>
/// 根据枚举获取DrpDownList
/// </summary>
/// <param name="ty"></param>
/// <param name="name"></param>
/// <param name="isAll"></param>
/// <returns></returns>
public static MvcHtmlString DrpDownByEnum(this HtmlHelper html, Type ty, string name = "Status", bool isAll = true)
{
var sbHtml = new StringBuilder(string.Empty);
sbHtml.AppendFormat("<select class='form-control' name='{0}'>", name);
if (isAll)
{ sbHtml.AppendFormat("<option value='{0}'>{1}</option>",
"-1",
"==全部==");
}
var vals = Enum.GetValues(ty);
for (int i = ; i < vals.Length; i++)
{
var val = vals.GetValue(i);
var text = Enum.Parse(ty, val.ToString()).ToString();
sbHtml.AppendFormat("<option value='{0}'>{1}</option>",
(int)val,
text);
}
sbHtml.Append("</select>");
return MvcHtmlString.Create(sbHtml.ToString());
}

最后,定义一个枚举名称为ComStatus,然后在试图View中@using引用咋们自定义的扩展方法命名空间,这样咋们就能直接使用@html.DrpDownByEnum()我们定义的方法了,使用代码如下:

@Html.DrpDownByEnum(typeof(StageEnumHelper.ComStatus))

. 自定义ActionFilter,验证登陆和权限访问

首先,自定义个ActionFilter类名称为CheckActionLoginAttribute并且继承ActionFilterAttribute,重写OnActionExecuting方法,先来看如下整个实现的代码:

 /// <summary>
/// action验证登陆 + 菜单权限
/// </summary>
public class CheckActionLoginAttribute : ActionFilterAttribute
{ private bool _IsAuthor = false; /// <summary>
/// 是否验证访问权限(默认需要)
/// </summary>
/// <param name="IsAuthor"></param>
public CheckActionLoginAttribute(bool IsAuthor = true)
{
_IsAuthor = IsAuthor;
} public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Result = CheckLoginAndMenu(filterContext.ActionDescriptor, filterContext, _IsAuthor);
} /// <summary>
/// 验证登陆 + 菜单权限 方法
/// </summary>
/// <param name="descript">Action文档描述</param>
/// <param name="context">访问上下文</param>
/// <param name="IsAuthor">是否需要验证访问权限</param>
/// <returns></returns>
public static ActionResult CheckLoginAndMenu(ActionDescriptor descript, ControllerContext context, bool IsAuthor)
{
ActionResult result = null;
var returnUrl = context.HttpContext.Request.Path;
var session = CacheRepository.Current(CacheType.BaseCache).GetCache<StageModel.MoUserData>();
if (session == null)
{ //跳转登录页面
result = new RedirectResult(
string.Format("{0}?returnUrl={1}",
"/User/Login",
returnUrl
)
);
}
else if (IsAuthor)
{
//获取请求的Action路径
returnUrl = string.Format("/{0}/{1}",
descript.ControllerDescriptor.ControllerName,
descript.ActionName);
//验证是否有访问权限
var isAllow = session.Menus.Any(b => b.Link.ToUpper().IndexOf(returnUrl.ToUpper()) == );
if (!isAllow)
{
//无访问权限
result = new RedirectResult(
string.Format("{0}",
"/Error"
)
);
}
}
return result;
}
}

然后,分析代码可以看到我们提取了一个方法

public static ActionResult CheckLoginAndMenu(ActionDescriptor descript, ControllerContext context, bool IsAuthor)

参数说明:ActionDescriptor:用来获取路由请求的Action和Controller的名称信息;ControllerContext:用来获取当前请求的路由地址,方便后面登陆成功后直接跳转到该次访问的路由地址;bool参数:用来控制是否需要做菜单访问权限的验证;

逻辑说明:

1.咋们做登陆验证的时候使用缓存工厂CacheRepository获取Session数据,如果为null不存在直接通过ActionResult返回跳转登录页面结果,这个结果传递给OnActionExecuting重写时的参数ActionExecutingContext的Result属性,这样就能验证没有登录跳转到登陆页面去并且通过returnUrl参数传递给登陆页面,登录成功后跳转此路由地址

2.由自定义构造函数传入是否需要验证菜单访问权限,这样方便配置管理,有人会问为什么不直接都验证访问权限呢,这个看不通的需求吧;菜单权限验证主要是根据用户访问的路由地址Controller+Action与登陆用户session保存的菜单权限的路径地址相互对比,以此来做菜单权限验证,这里要获取Action,Controller路由名称主要是通过ActionExecutingContext.ActionDescriptor参数获取到的;

最后,来看下CheckActionLogin过滤器怎么调用:

/// <summary>
/// 用户中心
/// </summary>
/// <returns></returns>
[CheckActionLogin]
public ActionResult UserCenter()
{
var userData = CacheRepository.Current(CacheType.BaseCache).GetCache<StageModel.MoUserData>();
return PartialView(userData);
} /// <summary>
/// 修改密码
/// </summary>
/// <returns></returns>
[CheckActionLogin(false)]
public ActionResult ChangeUser()
{
var userData = CacheRepository.Current(CacheType.BaseCache).GetCache<StageModel.MoUserData>();
return PartialView(userData);
}

效果图:

. 扩展HtmlHelper,无限递归生成菜单栏

首先,因为这里和第一节点讲述的内容都使用了HtmlHelper来扩展,扩展步奏就不多说了,主要来看递归的方法,递归名称解释就是自己可以调用自己,无限的深入层级,我们先来看整体方法:

 /// <summary>
/// 获取menu菜单html格式
/// </summary>
/// <param name="menusId">角色对应的权限Id集合</param>
/// <param name="listMenu">第一次全部菜单数据</param>
/// <param name="parentName">父级菜单的名称,多个层级关系名称使用‘|’拼接</parentName>
/// <param name="ulClass"></param>
/// <returns></returns>
public static string GetMenuHtml(List<StageModel.MoMenu> menus, List<StageModel.MoMenu> listMenu = null, string parentName = "", string ulClass = "nav nav-list")
{ //全部菜单数据,第一次全部菜单数据
listMenu = listMenu ?? StageClass.GetAllMenus();
if (listMenu == null || listMenu.Count <= || menus.Count <= ) { return ""; } //获取当前请求路径
var currentPath = HttpContext.Current.Request.Path;
//html结构
var sbHtml = new StringBuilder(string.Empty); sbHtml.AppendFormat("<ul class='submenu {0}'>", ulClass);
foreach (var item in listMenu)
{ //查询是否有对应Id的菜单
var isExists = menus.Any(b => b.Id == item.Id);
var nowName = parentName + "|" + item.Name;
if (!isExists)
{
//不存在
if ((item.ListMenu == null || item.ListMenu.Count <= )) { continue; }
//还有子级,继续递归
var currentNav = currentPath.Contains(item.Link) ? "nav-show" : "nav-hide"; sbHtml.AppendFormat(@" <li class=''>
<a href='#' data-url='{0}' class='dropdown-toggle'>
<i class='menu-icon fa {3}'></i>
<span class='menu-text'> {1} </span>
<b class='arrow fa fa-angle-down'></b>
</a>
<b class='arrow'></b>
{2}
</li>
",
"",
item.Name,
GetMenuHtml(menus, item.ListMenu, nowName, currentNav),
string.IsNullOrEmpty(item.Icon) ? StageModel.MoIcon.Default : item.Icon);
}
else
{ //存在
//无子级,本级已经是最后一级
sbHtml.AppendFormat(@" <li class=''>
<a href='#' data-url='{0}' data-menus='{3}' data-des='{4}'>
<i class='menu-icon fa {2}'></i>
<span class='menu-text'> {1} </span>
</a> <b class='arrow'></b>
</li>
",
item.Link,
item.Name,
string.IsNullOrEmpty(item.Icon) ? StageModel.MoIcon.Default : item.Icon,
nowName,
item.Des);
} }
sbHtml.Append("</ul>"); return sbHtml.ToString();
}

参数说明:

第一个List<StageModel.MoMenu> menus:这里传递的是登陆用户具有的菜单权限集合

第二个List<StageModel.MoMenu> listMenu:本次循环的菜单集合(第一次进入方法的时候,这里传递的是系统所有菜单的集合数据)

string parentName:父级菜单的名称,多个层级关系名称使用‘|’拼接,主要用处是在页面点击节点的时候可以直接获取此节点的层级节点名称,方便展示

string ulClass = "nav nav-list":css样式控制参数(这里使用的是Ace样式)

方法体里面的代码,每个关键点和思路都有备注,大家可以查看下

最后,咋们来看一下效果截图:

. Global中增加全局Application_Error监控404异常

首先,这里Application_Error监控对于mvc就好增加了,直接在Global里面增加代码如:

 /// <summary>
/// 捕获不到达Action的错误
/// </summary>
protected void Application_Error()
{ var lastError = Server.GetLastError();
if (lastError != null)
{
var httpError = lastError as HttpException;
if (httpError != null)
{
switch (httpError.GetHttpCode())
{
case :
Response.Redirect("/Error");
break;
}
}
}
}

这样就能获取出用户访问不存在路由时候提示的404code错误,然后跳转到自定义路由Error中去,我们这里测试访问一个我这里存在的地址:http://localhost:5050/home 和不存在的地址:http://localhost:5050/home1,后者会自动跳转到我定义的Error路由对应的试图中去,效果大家可以自行体验;

上面是全局的404错误,那么Action出错怎么获取信息呢,我们可以重写HandleErrorAttribute中的void OnException(ExceptionContext filterContext)方法,这里直接给出具体代码:

/// <summary>
/// 捕获Action错误
/// </summary>
public class ExceptionPageAttribute : HandleErrorAttribute
{ public override void OnException(ExceptionContext filterContext)
{
var code = ;
var lastError = filterContext.Exception;
if (lastError != null)
{
var httpError = lastError as HttpException;
if (httpError != null)
{
code = httpError.GetHttpCode();
}
} filterContext.Result = new RedirectResult("/Error?code=" + code);
}
}

自定义完后,我们需要在项目根目录下的App_Start/FilterConfig.cs文件中的void RegisterGlobalFilters(GlobalFilterCollection filters)方法中添加我们的自定义错误拦截器,这样如果Action或者Controller提示异常的时候会自动进入自定义ExceptionPage拦截器方法中,跳转到我们的Error路由视图中去;

. 实现及使用缓存工厂(最新缓存工厂代码在上一篇分享的缓存工厂之Redis缓存)

在做后台系统的时候用到了前面封装的缓存工厂,这里主要是拿过来修改了部分信息,最新代码已经更新到上篇缓存工厂之Redis缓存文章中,如果有需要的朋友可以点击链接;

本次的分享就到这里了,不知不觉凌晨了,该说睡觉的时候了,谢谢各位观赏,欢迎多多点赞。

MVC常遇见的几个场景代码分享的更多相关文章

  1. 瞧一瞧,看一看呐,用MVC+EF快速弄出一个CRUD,一行代码都不用写,真的一行代码都不用写!!!!

    瞧一瞧,看一看呐用MVC+EF快速弄出一个CRUD,一行代码都不用写,真的一行代码都不用写!!!! 现在要写的呢就是,用MVC和EF弄出一个CRUD四个页面和一个列表页面的一个快速DEMO,当然是在不 ...

  2. ASP.NET MVC 5 学习教程:生成的代码详解

    原文 ASP.NET MVC 5 学习教程:生成的代码详解 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 ...

  3. Java生鲜电商平台-生鲜售后系统的退款架构设计与代码分享

    Java生鲜电商平台-生鲜售后系统的退款架构设计与代码分享 说明:任何一个电商行业都涉及到退货与退款的问题,但是生鲜电商行业还设有一个显著的特点,那就是换货.在人性面前,各种各样的退货,退款,换货的售 ...

  4. JAVA基础代码分享--求圆面积

    问题描述 用户输入圆的半径,计算并显示圆的面积 代码分享 /** * @author hpu-gs * 2015/11/25 */ public class Circle { public stati ...

  5. JAVA基础代码分享--DVD管理

    问题描述 为某音像店开发一个迷你DVD管理器,最多可存6张DVD,实现碟片的管理. 管理器具备的功能主要有: 1.查看DVD信息. 菜单选择查看功能,展示DVD的信息. 2.新增DVD信息 选择新增功 ...

  6. JAVA基础代码分享--学生成绩管理

    问题描述: 从键盘读入学生成绩,找出最高分,并输出学生成绩等级. 成绩>=最高分-10  等级为’A’   成绩>=最高分-20  等级为’B’ 成绩>=最高分-30  等级为’C’ ...

  7. jQuery插件库代码分享 - 进阶者系列 - 学习者系列文章

    这些天将原来在网上找的jQuery插件进行了下整理,特此将代码分享出来给大家. 见下图结构. 对目录结构进行了分类.这里是插件列表. 这里总共收集了20来个插件.还有下面未进行划分的. 下面是DEMO ...

  8. .net之工作流工程展示及代码分享(四)主控制类

    现在应该讲主控制类了,为了不把系统弄得太复杂,所以就用一个类作为主要控制类(服务类),作为前端.后端.业务逻辑的控制类. WorkflowService类的类图如下: 该类的构造函数: public ...

  9. .net之工作流工程展示及代码分享(三)数据存储引擎

    数据存储引擎是本项目里比较有特色的模块. 特色一,使用接口来对应不同的数据库.数据库可以是Oracle.Sqlserver.MogoDB.甚至是XML文件.采用接口进行对应: public inter ...

随机推荐

  1. C#异步编程(一)

    异步编程简介 前言 本人学习.Net两年有余,是第一次写博客,虽然写的很认真,当毕竟是第一次,肯定会有很多不足之处, 希望大家照顾照顾新人,有错误之处可以指出来,我会虚心接受的. 何谓异步 与同步相对 ...

  2. Microservice架构模式简介

    在2014年,Sam Newman,Martin Fowler在ThoughtWorks的一位同事,出版了一本新书<Building Microservices>.该书描述了如何按照Mic ...

  3. CRL快速开发框架系列教程五(使用缓存)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  4. winform 窗体圆角设计

    网上看到的很多winform窗体圆角设计代码都比较累赘,这里分享一个少量代码就可以实现的圆角.主要运用了System.Drawing.Drawing2D. 效果图 代码如下. private void ...

  5. RabbitMQ + PHP (一)入门与安装

    RabbitMQ: 1.是实现AMQP(高级消息队列协议)的消息中间件的一种. 2.主要是为了实现系统之间的双向解耦而实现的.当生产者大量产生数据时,消费者无法快速消费,那么需要一个中间层.保存这个数 ...

  6. 初识JavaScript

    JavaScript ECMA-262: 变量,函数,对象,数据类型....唯独没有输入和输出. Javascript:包含 ECMA-262,核心 BOM 浏览器对象模型, DOM 文档对象模型 什 ...

  7. CSS三个定位——常规、浮动、绝对定位

    .dage { width: 868px; background: #5B8C75; border: 10px solid #A08C5A; margin-top: -125px; margin-le ...

  8. Oracle 列数据聚合方法汇总

    网上流传众多列数据聚合方法,现将各方法整理汇总,以做备忘. wm_concat 该方法来自wmsys下的wm_concat函数,属于Oracle内部函数,返回值类型varchar2,最大字符数4000 ...

  9. Android快乐贪吃蛇游戏实战项目开发教程-03虚拟方向键(二)绘制一个三角形

    该系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html 一.绘制三角形 在上一篇文章中,我们已经新建了虚拟方向键的自定义控件Direct ...

  10. 程序员装B指南

    一.准备工作 "工欲善其事必先利其器." 1.电脑不一定要配置高,但是双屏是必须的,越大越好,能一个横屏一个竖屏更好.一个用来查资料,一个用来写代码.总之要显得信息量很大,效率很高 ...