Fireasy与Asp.net MVC结合

 

Fireasy之前都是使用HttpService来为jquery ajax提供服务,这个HttpService实际上和MVC的原理机制是一样的,只是它支持两种方式,一种是使用统一的一个类来提供服务(基于MEF导入),另一种是使用aspx的类文件提供服务,具体使用哪一种,根据项目的性质来决定。

Asp.net MVC也就了解了一些皮毛,还不是很熟悉,正在深度学习中。不过基于以前的开发习惯,我觉得MVC要进行以下几点的改进:

(1)异常处理。MVC实现了一个异常滤过器HandleErrorAttribute,可以对执行action发生的异常进行处理,以便返回错误信息。但是它默认返回了一个View,我希望在发生异常时记录日志,并在前台进行友好的提示。

(2)Json序列化。翻出JsonResult的源代码来看,你会发现它的json序列化是由JavaScriptSerializer类完成的,对于这个类,我不想说什么。

(3)Action参数的反序列化。对于Fireasy的轻实体模型而言,它无法进行反序列化。

暂时先处理这三个问题吧,后来遇到问题再贴上来。

      一、异常处理

在之前的开发模式中,我们都是使用ajax取数据和保存数据,我觉得相对于mvc的model bind来说这样比较灵活,因为毕竟使用easyui,在取数的时候可能会有一些复杂的处理,我们都放在前台上编码吧。

在读取和保存数据的时候,避免不了会发生一些异常,如果按mvc的默认处理,我们将无法接收异常信息,这是一样相当头疼的事情。因此,我们对HandleErrorAttribute做一些改进:

对json的包装一会儿会说。如果是ClientNotificationException异常,直接显示错误信息,否则记录日志,返回提示信息。

    /// <summary>
/// 控制器方法执行发生异常时,记录日志并返回友好的提示信息。
/// </summary>
public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
/// <summary>
/// 处理异常信息。
/// </summary>
/// <param name="filterContext"></param>
public override void OnException(ExceptionContext filterContext)
{
if (IsJsonResult(filterContext))
{
HandleExceptionForJson(filterContext);
}
else
{
HandleException(filterContext);
} LogException(filterContext);
} /// <summary>
/// 判断返回结果是否为 Json 类型。
/// </summary>
/// <param name="filterContext"></param>
/// <returns></returns>
protected virtual bool IsJsonResult(ExceptionContext filterContext)
{
if (ActionContext.Current != null)
{
var desc = ActionContext.Current.ActionDescriptor as ReflectedActionDescriptor;
if (desc != null)
{
return typeof(JsonResult).IsAssignableFrom(desc.MethodInfo.ReturnType);
}
} return false;
} /// <summary>
/// 处理返回结果为Json的异常信息。
/// </summary>
/// <param name="filterContext"></param>
protected virtual void HandleExceptionForJson(ExceptionContext filterContext)
{
//如果是通知类的异常,直接输出提示
var notifyExp = filterContext.Exception as Fireasy.Common.ClientNotificationException;
if (notifyExp != null)
{
filterContext.Result = new JsonResultWrapper(new JsonResult { Data = Result.Fail(notifyExp.Message) });
filterContext.ExceptionHandled = true;
return;
}
else
{
filterContext.Result = GetHandledResult();
filterContext.ExceptionHandled = true;
}
} /// <summary>
/// 处理一般返回结果的异常信息。
/// </summary>
/// <param name="filterContext"></param>
protected virtual void HandleException(ExceptionContext filterContext)
{
var errorPage = ConfigurationManager.AppSettings["error-page"];
if (!string.IsNullOrEmpty(errorPage))
{
filterContext.Result = new RedirectResult(errorPage);
filterContext.ExceptionHandled = true;
}
} /// <summary>
/// 记录异常日志。
/// </summary>
/// <param name="filterContext"></param>
protected virtual void LogException(ExceptionContext filterContext)
{
//记录日志
var logger = LoggerFactory.CreateLogger();
if (logger != null)
{
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"]; logger.Error(string.Format("执行控制器 {0} 的方法 {1} 时发生错误。",
controllerName, actionName), filterContext.Exception);
}
} /// <summary>
/// 获取处理后的返回结果。
/// </summary>
/// <returns></returns>
protected virtual ActionResult GetHandledResult()
{
if (ActionContext.Current != null)
{
//检查是否定义了 ExceptionBehaviorAttribute 特性
var attr = ActionContext.Current.ActionDescriptor
.GetCustomAttributes(typeof(ExceptionBehaviorAttribute), false)
.Cast<ExceptionBehaviorAttribute>().FirstOrDefault(); if (attr != null)
{
//返回空数组,一般用在列表绑定上
if (attr.EmptyArray)
{
return new JsonResultWrapper(new JsonResult { Data = new string[0] });
}
//使用提示信息
else if (!string.IsNullOrEmpty(attr.Message))
{
return new JsonResultWrapper(new JsonResult { Data = Result.Fail(attr.Message) });
}
}
} return new JsonResultWrapper(new JsonResult { Data = Result.Fail("发生错误,请查阅相关日志或联系管理员。") });
}
}

对异常的处理分两种情况,如果是返回Json,则返回结果是Json格式,否则定向到出错页面

      二、Json序列化

Json的序列化是在JsonResult的ExecuteResult里完成,它用JavaScriptSerializer这个东东来序列化,有点想不通,webapi都使用Json.Net了,mvc5还是这样,估计MS不想用Json.Net。

实在不想在Action里使用其他的Result类来替代Json()方法,因为这样做会破坏程序的可维护性。那么最好的办法就是对JsonResult进行包装。

    /// <summary>
/// 对 <see cref="JsonResult"/> 进行包装,重写序列化对象的方法。
/// </summary>
public class JsonResultWrapper : JsonResult
{
private JsonResult result; /// <summary>
/// 初始化 <see cref="JsonResultWrapper"/> 类的新实例。
/// </summary>
/// <param name="result"></param>
public JsonResultWrapper(JsonResult result)
{
result.JsonRequestBehavior = System.Web.Mvc.JsonRequestBehavior.AllowGet;
this.result = result;
} /// <summary>
/// 将结果输出到 Response。
/// </summary>
/// <param name="context"></param>
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
} if (result == null)
{
throw new ArgumentNullException("result");
} if ((result.JsonRequestBehavior == System.Web.Mvc.JsonRequestBehavior.DenyGet) &&
string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("不能在Url中访问。");
} var response = context.HttpContext.Response;
if (!string.IsNullOrEmpty(result.ContentType))
{
response.ContentType = result.ContentType;
}
else
{
response.ContentType = "application/json";
} if (result.ContentEncoding != null)
{
response.ContentEncoding = result.ContentEncoding;
} if (result.Data != null)
{
response.Write(SerializeJson(context, result.Data));
}
} /// <summary>
/// 将数据序列化为 Json 字符串。这里使用了 Fireasy 提供的 Json 序列化方法。
/// </summary>
/// <param name="context"></param>
/// <param name="data">要序列化的数据。</param>
/// <returns></returns>
protected virtual string SerializeJson(ControllerContext context, object data)
{
var option = new JsonSerializeOption();
if (ActionContext.Current != null)
{
//json转换器
var converters = ActionContext.Current.Converters.Where(s => s is JsonConverter).Cast<JsonConverter>();
option.Converters.AddRange(converters);
} //jsonp的处理
//var jsoncallback = context.HttpContext.Request.Params["callback"]; var serializer = new JsonSerializer(option);
var json = serializer.Serialize(data); //if (!string.IsNullOrEmpty(jsoncallback))
//{
// return string.Format("{0}({1})", jsoncallback, json);
//} return json;
}
}

注意了,上面代码中标红的ActionContext是一个线程内的上下文对象,它的作用是在方法执行期间可以由用户在Action里得到该对象并往里面加东西,比如JsonConverter。

    /// <summary>
/// 控制器操作方法执行期间的上下文对象。
/// </summary>
public class ActionContext : Scope<ActionContext>
{
internal ActionContext(ControllerContext controllerContext)
{
ControllerContext = controllerContext;
Converters = new List<ITextConverter>();
} /// <summary>
/// 获取控制器上下文对象。
/// </summary>
public ControllerContext ControllerContext { get; private set; } /// <summary>
/// 获取动作相关的 <see cref="ActionDescriptor"/> 对象。
/// </summary>
public ActionDescriptor ActionDescriptor { get; internal set; } /// <summary>
/// 获取文本转换器列表。
/// </summary>
public List<ITextConverter> Converters { get; private set; } /// <summary>
/// 获取动作方法的参数字典。
/// </summary>
public IDictionary<string, object> Parameters { get; internal set; }
}

那么它在什么时候被创建,什么时候被销毁呢,幸好有ControllerActionInvoker这个类可以进行继承。

    public class ControllerActionInvoker : System.Web.Mvc.ControllerActionInvoker
{
internal static ControllerActionInvoker Instance = new ControllerActionInvoker(); /// <summary>
/// 执行控制器的动作。
/// </summary>
/// <param name="controllerContext"></param>
/// <param name="actionName"></param>
/// <returns></returns>
public override bool InvokeAction(ControllerContext controllerContext, string actionName)
{
using (var scope = new ActionContext(controllerContext))
{
return base.InvokeAction(controllerContext, actionName);
}
} /// <summary>
/// 执行动作方法。
/// </summary>
/// <param name="controllerContext"></param>
/// <param name="actionDescriptor"></param>
/// <param name="parameters"></param>
/// <returns></returns>
protected override ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
{
if (ActionContext.Current != null)
{
ActionContext.Current.ActionDescriptor = actionDescriptor;
ActionContext.Current.Parameters = parameters;
} var result = base.InvokeActionMethod(controllerContext, actionDescriptor, parameters);
var jsonResult = result as JsonResult;
if (jsonResult != null && !(result is JsonResultWrapper))
{
return WrapJsonResult(jsonResult);
} return result;
} /// <summary>
/// 对 <see cref="JsonResult"/> 对象进行包装并转换输出结果。
/// </summary>
/// <param name="jsonResult"></param>
/// <returns></returns>
protected virtual ActionResult WrapJsonResult(JsonResult jsonResult)
{
return new JsonResultWrapper(jsonResult);
}
}

这样,在Action方法里,都可以通过ActionContext.Current获得此实例,可获得ActionDescriptor等等定义信息,以及序列化转换器。

另外,上面的代码中,重写InvokeActionMethod方法,对JsonResult进行包装。

如果你是使用Json.Net,那一样的道理,换成Json.Net的序列化就行了。

      三、Action参数反序列化

在上面的这个类ControllerActionInvoker中,重写GetParameterValue方法,对参数进行反序列化。

    public class ControllerActionInvoker : System.Web.Mvc.ControllerActionInvoker
{
internal static ControllerActionInvoker Instance = new ControllerActionInvoker(); /// <summary>
/// 获取参数的值。
/// </summary>
/// <param name="controllerContext"></param>
/// <param name="parameterDescriptor"></param>
/// <returns></returns>
protected override object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
{
var value = base.GetParameterValue(controllerContext, parameterDescriptor);
if (value == null)
{
//对json进行反序列化,由于使用了基于 Fireasy AOP 的实体模型,所以必须使用 Fireasy 的反序列化方法
var json = controllerContext.HttpContext.Request.Params[parameterDescriptor.ParameterName];
if (!string.IsNullOrEmpty(json))
{
try
{
var option = new JsonSerializeOption();
if (ActionContext.Current != null)
{
//json转换器
var converters = ActionContext.Current.Converters.Where(s => s is JsonConverter).Cast<JsonConverter>();
option.Converters.AddRange(converters);
} var serializer = new JsonSerializer(option);
value = serializer.Deserialize(json, parameterDescriptor.ParameterType);
}
catch (Exception exp)
{
var logger = LoggerFactory.CreateLogger();
if (logger != null)
{
var message = string.Format("无法解析控制器 {0} 的方法 {1} 的参数 {2} 的值。\n\n数据为: {3}",
parameterDescriptor.ActionDescriptor.ControllerDescriptor.ControllerName,
parameterDescriptor.ActionDescriptor.ActionName,
parameterDescriptor.ParameterName,
json); logger.Error(message, exp);
}
}
}
} return value;
}
}

改造完成,现在沿用以前的开发模式,但是底层使用了MVC的控制器,也算是一点小进步吧。昨天一讲课一边做了一个小实例,我也放上来分享给大家,以加深对Fireasy的了解。

示例下载

作者:黄旭东
出处:http://fireasy.cnblogs.com 

Fireasy的更多相关文章

  1. Fireasy新版本发布

    1.5.40.42028  2015-2-4 ** Fireasy.Common 1.DynamicBuilder新增使用表达式SetCustomAttribute的重载方法 2.DateTimeEx ...

  2. 基于 fireasy 构建的 asp.net core 示例

    最近花时间弄了一个关于fireasy使用的demo,已放到 github 上供大家研究,https://github.com/faib920/zero 该 demo 演示了如何使用 fireasy 创 ...

  3. fireasy 使用篇 - 简介

    一.Fireasy 简介 Fireasy是一套基于.Net Framework应用开发组件,其主旨思想为“让开发变为更简单”,其义为,使用尽可能少的组件,实现你所需的功能.Fireasy几乎覆盖了开发 ...

  4. 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇

    ==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...

  5. 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之三 —— 配置

    ==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...

  6. 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— 准备

    ==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...

  7. Winform快速开发组件的实现(二)

    昨天我们一直在做准备工作,最终表单数据需要从数据库里提取,并保存到数据库,今天接着介绍如何做提取.保存和验证. 四.提取并显示信息 在EditForm我们定义一个InfoId属性,用于接收在列表页面打 ...

  8. Winform快速开发组件的实现(一)

    好久好久没有露面了,呵呵,对于写文章都有点生疏了. 在拿到任何一个项目,不管是b/s的还是c/s,我不会立即开始写代码,我一般会为使这些项目能够快速开发制定一系列的支持组件,虽然可能前期会付出一些代价 ...

  9. 盘点几种数据库的分页SQL的写法(转)

    Data序列——盘点几种数据库的分页SQL的写法http://www.cnblogs.com/fireasy/archive/2013/04/10/3013088.html

随机推荐

  1. HDU 1815, POJ 2749 Building roads(2-sat)

    HDU 1815, POJ 2749 Building roads pid=1815" target="_blank" style="">题目链 ...

  2. 再谈Hibernate级联删除——JPA下的Hibernate实现一对多级联删除CascadeType.DELETE_ORPHAN

    声明: 1.本文系原创,非抄袭或转载过来的. 2.本文论点都亲手做过实验论证. 3.本文所讲的Hibernate配置都基于注解的方式,hbm语法未提供. 非常多人对持久层概念搞不清JPA.Hibern ...

  3. 【Java GUI】Java面板基础:JPanel

    有两个面板,常见的面板(JPanel)和滚动面板(JScrollPane) Jpanel 面板是一种常见的容器,JPanel的作用是实现接口层次结构,面放入一些组件.也能够在上面绘画,将放有组件和有画 ...

  4. __weak如何实现目标值自己主动设置nil的

    在开始评论__weak机制之前,首先,一些床上用品 ARC 实现 苹果公司的官方介绍说,.ARC这是"内存管理由编译器"的,但事实上,只有编译器不能完全胜任,ARC另外还要看OC执 ...

  5. 使用WebBrowser,内存一直增加的解决办法

    -- in class definition [DllImport("KERNEL32.DLL", EntryPoint = "SetProcessWorkingSetS ...

  6. 经纪xx系统节点VIP案例介绍和深入分析异常

    系统环境    硬件平台 &  操作 IBM 570 操作系统版本号  AIX 5.3 物理内存  32G Oracle 产品及版本号  10.2.0.5 RAC 业务类型  OLTP 背 ...

  7. 简单实现Android平台多语言

    这里,我们认识到两种语言.中国简体和繁体中国. 在res文件建议两个文件夹 values-zh-rCN values-zh-rTW 两个目录下都有一个strings.xml文件. 两个同名文件的字符串 ...

  8. 【C++知识汇总】运营商 &amp; 运算符重载

    [运算符]        在进行运算时,假设右括号的话我们知道先运算哪个,那假设没有括号了.算术运算符,关系运算符,逻辑运算符,位运算符.赋值运算符,++.--运算符等等,那么多的运算符.我们先算哪边 ...

  9. 有关windows在调试ODOO8.0有些问题

    继Ubuntu建筑物8.0调试环境后,,尝试windows设置开发环境. 最后的调试和开发,或将返回Linux环境,由于前一段时间手贱,改变分区表,该grub搞哈.哎!后来重建mbr,手动检索分区表( ...

  10. vs2010公布时去除msvcp100.dll和msvcr100.dll图讲解明

    近期开发个程序,Copy到虚拟机环境中測试时提示缺少msvcr100.dll,于是想到编译时设置选项去除依赖. 什么是 msvcr100.dll MS = Microsoft V = Visual C ...