运用custom actionResults 进一步增强MVC3
最近带我的师傅发给我一篇英文技术文档,抱着学习英文的心情我将原文翻译了一下。
具体内容如下:
上周我写了一篇关于“运用custom actionResults 进一步增强MVC3”的教程,如果您对于一些概念还不熟悉,请略过此文章。
一个评论人在我上一篇文章中提了个很好的问题,从而激发了我对源码的改进。非常感谢他的建议。
概述如下
我们的目的是要建立一个能运行在所有浏览器上的“Contact Us”的Form,并且通过使能Javascript提供一个增强的体验。
运用javascript:
如图所示,当浏览者浏览我们的使能Javascript网站点击“Contact Us”链接,网站将展示给他们一个漂亮的jquery UI 对话框。浏览者可以填写form同时在对话框内得到一个漂亮的确认信息,最后,在不离开此页面的情况下关闭它。
不运用javascript:
不使用javascript时,访问者仍然能够享用同样的功能,仅仅是用户体验弱一些。没有javascript逻辑功能的“Contact Us”就像一个单调过时的超链接,将用户导航到一个新的页面。当填写完表单点击“Send Message”,网站将会有确认提示并且重新跳转到主页。
新的Controller:
依照评论人的意见,我将逻辑抽象为两个用户“ActionResults”名为: AjaxableViewResult和AjaxableActionResult。假设命名我们的视图时遵守一些约定,新的Controller将会更简洁可读性更强。
[HttpGet] public ActionResult ContactUs() { return new AjaxableViewResult(); } [HttpPost] public ActionResult ContactUs(ContactUsInput input) { if (!ModelState.IsValid) return new AjaxableViewResult(input); // TODO: A real app would send some sort of email here TempData["Message"] = string.Format("Thanks for the feedback, {0}! We will contact you shortly.", input.Name); return new AjaxableActionResult { DefaultResult = () => RedirectToAction("Index"), AjaxResult = () => PartialView("_ThanksForFeedback", input) }; }
编码
通过下面的代码,您可以找到我们进一步增强的逻辑。如果您熟悉MVC中ActionResults的概念,下面的代码有记录的,且相对直接。
AjaxableActionResult: /// <summary> /// Executes a specific action result depending on whether the incoming request is an Ajax request or not /// </summary> public class AjaxableActionResult : ActionResult { /// <summary> /// The result to execute for non-Ajax requests /// </summary> public Func<ActionResult> DefaultResult { get; set; } /// <summary> /// The result the execute for Ajax requests /// </summary> public Func<ActionResult> AjaxResult { get; set; } public override void ExecuteResult(ControllerContext context) { if(DefaultResult == null) throw new ArgumentException("The DefaultResult property must be set"); if (AjaxResult == null) throw new ArgumentException("The AjaxResult property must be set"); if (context.HttpContext.Request.IsAjaxRequest()) { AjaxResult().ExecuteResult(context); } else { DefaultResult().ExecuteResult(context); } } } AjaxableViewResult: /// <summary> /// Inspects the incoming request and returns a PartialViewResult for Ajax requests and a full ViewResult for non-Ajax requests /// </summary> public class AjaxableViewResult : ActionResult { /// <summary> /// Determines the convention for looking up a PartialView for the incoming request. /// The default convention looks for an underscore followed by the action name. /// For example: _ContactUs.cshtml or _ContactUs.ascx /// </summary> public static Func<ControllerContext, string> AjaxViewNameConvention = context => "_" + context.RouteData.GetRequiredString("action"); /// <summary> /// The view name for non-Ajax requests /// </summary> public string NonAjaxViewName { get; set; } /// <summary> /// The view name for Ajax requests /// </summary> public string AjaxViewName { get; set; } /// <summary> /// The model that is rendered to the view /// </summary> public object Model { get; set; } /// <summary> /// Creates a new AjaxableViewResult using the default conventions /// </summary> public AjaxableViewResult() : this(null, null, null) { } /// <summary> /// Creates a new AjaxableViewResult using the default conventions with custom model data /// </summary> public AjaxableViewResult(object model) : this(null, null, model) { } /// <summary> /// Creates a new AjaxableViewResult with a specific ajax partial view /// </summary> public AjaxableViewResult(string ajaxViewName) : this(ajaxViewName, null) { } /// <summary> /// Creates a new AjaxableViewResult with a specific ajax partial view and model /// </summary> public AjaxableViewResult(string ajaxViewName, object model) : this(ajaxViewName, null, model) { } /// <summary> /// Creates a new AjaxableViewResult with a specific ajax view, non-ajax view, and model /// </summary> public AjaxableViewResult(string ajaxViewName, string defaultViewName, object model) { NonAjaxViewName = defaultViewName; AjaxViewName = ajaxViewName; Model = model; } public override void ExecuteResult(ControllerContext context) { context.Controller.ViewData.Model = Model; if (context.HttpContext.Request.IsAjaxRequest()) { var view = new PartialViewResult { ViewName = GetAjaxViewName(context), ViewData = context.Controller.ViewData }; view.ExecuteResult(context); } else { var view = new ViewResult { ViewName = GetViewName(context), ViewData = context.Controller.ViewData }; view.ExecuteResult(context); } } private string GetViewName(ControllerContext context) { return !string.IsNullOrEmpty(NonAjaxViewName) ? NonAjaxViewName : context.RouteData.GetRequiredString("action"); } private string GetAjaxViewName(ControllerContext context) { return !string.IsNullOrEmpty(AjaxViewName) ? AjaxViewName : AjaxViewNameConvention(context); } }
重写惯例AjaxViewName:
为了完整,我决定添加一个简单的可扩展点来修改查找约定Ajax view。在您的Global.asax文件中,你可以用静态AjaxViewNameConvention来定义您自己的partial view约定,看起来是在action name前加了个下划线。