• Fireasy


    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} 的值。
    
    数据为: {3}", 
                                    parameterDescriptor.ActionDescriptor.ControllerDescriptor.ControllerName, 
                                    parameterDescriptor.ActionDescriptor.ActionName, 
                                    parameterDescriptor.ParameterName,
                                    json);
    
                                logger.Error(message, exp);
                            }
                        }
                    }
                }
    
                return value;
            }
        }
    复制代码

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

    示例下载

    作者:黄旭东
    出处:http://fireasy.cnblogs.com 
  • 相关阅读:
    UIView背景渐变三种方法
    Phone漂亮的动画
    图片处理代码片段
    UIProgressView进度条
    UIActivityIndicatorView
    NSTimer定时器类
    iPhone Tableview分批显示数据
    无限剑制
    兔子繁殖(easy)
    植树节
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4518746.html
Copyright © 2020-2023  润新知