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. 认识Backbone (四)

    Backbone.View(视图) 视图的核心是处理数据业务逻辑.绑定DOM元素事件.渲染模型或者集合数据. 添加DOM元素  render view.render() render 默认实现是没有操 ...

  2. 编译命令行终端 swift

    So, this is where swift lives, after you've installed XCode 6 Beta: /Applications/Xcode6-Beta.app/Co ...

  3. [LeetCode119]Pascal's Triangle II

    题目: Given an index k, return the kth row of the Pascal's triangle. For example, given k = 3,Return [ ...

  4. [LeetCode66]Plus One

    题目: Given a non-negative number represented as an array of digits, plus one to the number. The digit ...

  5. unity简易小地图的实现(NGUI)

    首先,我们在场景中添加一个摄像机叫做minimapCamera, 把上面的Audio Listener组件去掉,调整摄像机位置,将其置于角色正上方,如图 新建一个Texture我们叫做minimapT ...

  6. Zen Coding in Visual Studio 2012

    http://www.johnpapa.net/zen-coding-in-visual-studio-2012 Zen Coding is a faster way to write HTML us ...

  7. [精华]Hadoop,HBase分布式集群和solr环境搭建

    1. 机器准备(这里做測试用,目的准备5台CentOS的linux系统) 1.1 准备了2台机器,安装win7系统(64位) 两台windows物理主机: 192.168.131.44 adminis ...

  8. Kienct与Arduino学习笔记(2) 深度图像与现实世界的深度图的坐标

    转载请注明出处:http://blog.csdn.net/lxk7280 首先,要接触一下KinectOrbit这个摄像机库,这篇文章中有这个库的下载网址和简单的介绍:http://blog.csdn ...

  9. HDU 3065 病毒在继续 (AC自己主动机)

    中国标题不解释 Sample Input 3 AA BB CC ooxxCC%dAAAoen....END   Sample Output AA: 2 CC: 1 输出病毒出现的次数! #includ ...

  10. BZOJ 3589 动态树 树链拆分+纳入和排除定理

    标题效果:鉴于一棵树.每个节点有一个右值,所有节点正确启动值他们是0.有两种操作模式,0 x y代表x右所有点的子树的根值添加y. 1 k a1 b1 a2 b2 --ak bk代表质疑. 共同拥有者 ...