ASP.NET MVC: Should Controllers called by AJAX return JSON or rendered html?
我无法确定AJAX调用的控制器操作是应该返回部分视图还是"原始"JSON。
返回部分视图,使用呈现的HTML使javascript更容易使用返回的HTML更新当前DOM。 但是,它确实限制了使用Web服务的javascript客户端可以对返回的HTML执行的操作。
另一方面,让控制器操作返回JSON将需要javascript调用"手动"基于返回的JSON创建标记。
像往常一样,每种方法都有它的好处和缺点。 每种方法都有其他优点/缺点吗?
在我看来,返回JSON然后让客户端视图排序可能会因为以下限制而变得混乱:
我处理这个的方法是返回渲染的HTML,但是使用局部视图返回这个呈现的HTML。这为您提供了两全其美的体验。您已获得服务器端模板以及IntelliSense支持。
这是一个例子:
这是我的Ajax调用,因为你可以看到它所做的就是替换我的无序列表的html:
1 2 3 4 5 | FilterRequests: function() { $.post("/Request.aspx/GetFilteredRequests", { }, function(data) { $('ul.requests').html(data); }); }, |
这是我在我的控制器上的动作:
1 2 3 4 5 6 | public ActionResult GetFilteredRequests(string filterJson) { var requests = _requestDao.LoadAll(); return PartialView("FilteredRequests", requests); } |
最后,这是我的部分视图(没有必要理解这一点,我只是向您展示一些渲染在真实世界的应用程序中会有多复杂。我很害怕在JavaScript中执行此操作。您还会注意到我的局部视图反过来调用其他部分视图。):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Request>>" %> <%@ Import Namespace="Diangy.HelpDesk.Models.Lookups"%> <%@ Import Namespace="Diangy.HelpDesk.Models.Requests"%> <%@ Import Namespace="System.Web.Mvc.Html"%> <% foreach (var request in ViewData.Model) %> <%{%> <li class="request"> #<%= request.Id %>: <%= request.InitialInteraction().Description %> <p> from <%= request.Customer.FullName %> (<%= request.Customer.EmailAddress %>), <%= request.InitialInteraction().UsableTimeStamp %> </p> Custom Fields & Lookups <ul> <li> ">Custom Fields </li> <% foreach (var lookupDefinition in (List<LookupDefinition>)ViewData["LookupDefinitions"]) %> <%{%> <li> "><%= lookupDefinition.Name %> </li> <%}%> </ul> <% Html.RenderPartial("CustomFields", request); %> <% Html.RenderPartial("Discussion", request); %> <% Html.RenderPartial("Attachment", request); %> </li> <%}%> |
如果您正在使用MVC范例,控制器应该返回数据(JSON)并让视图为自己排序,就像它的工作是查找/调整模型中的数据并将其传递给服务器上的视图侧。
你得到了褐色点
-
保持逻辑和UI之间的关注点分离
-
使你的ajax动作可测试(祝你好运测试从该动作返回的HTML ......)
它可能有点复杂,但它很合适。
您可以使用客户端模板系统(例如MS Ajax Toolkit中现有的系统)来帮助承担一些负载并在客户端保留逻辑/呈现分离。
所以我肯定会说JSON。但是,嘿,YMMV像往常一样......
我想允许调用应用程序来决定。我把MultiViewController(我在网上发现的大部分代码,我会在找到它的时候尝试用信用卡更新)加在一起,基于动作扩展,它将返回相应的格式。例如:
1 2 3 4 5 6 | myapp.com/api/Users/1 - defaults to html based on route myapp.com/api/Users.html/1 - html myapp.com/api/Users.json/1 - json myapp.com/api/Users.xml/1 - xml myapp.com/api/Users.partial/1 - returns a partial view of action name (see code) myapp.com/api/Users.clean/1 - partial html without styling, etc... |
我的控制器继承自MultiViewController而不是"return view(Model);"我只是调用"返回FormatView(Model);或者FormatView("ViewName",Model);"。第二个,如果我需要将特定视图应用于结果 - 而不是隐含视图。
MultiViewController看起来像这样。特别注意FormatView,whitch返回一个动作结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | public abstract class MultiViewController : Controller { private const string FORMAT_KEY ="format"; public enum FileFormat {Html, Json, Xml, Partial, Clean} protected MultiViewController() { RequestedFormat = FileFormat.Html; } protected FileFormat RequestedFormat { get; private set; } protected override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); var routeValues = filterContext.RouteData.Values; if (routeValues.ContainsKey(FORMAT_KEY)) { var requestedFormat = routeValues[FORMAT_KEY].ToString(); if (isValidFormat(requestedFormat)) { RequestedFormat = (FileFormat)Enum.Parse(typeof(FileFormat), requestedFormat, true); } } } private bool isValidFormat(string requestedFormat) { return Enum.GetNames(typeof (FileFormat)).Any(format => format.ToLower() == requestedFormat.ToLower()); } protected ActionResult FormatView(string viewName, object viewModel) { switch (RequestedFormat) { case FileFormat.Html: if (viewName != string.Empty) { return View(viewName,viewModel); } return View(viewModel); case FileFormat.Json: return Json(viewModel); case FileFormat.Xml: return new XmlResult(viewModel); case FileFormat.Partial: //return View(this.ControllerContext.RouteData.Values["action"] +"Partial"); return PartialView(this.ControllerContext.RouteData.Values["action"] +"Partial"); case FileFormat.Clean: if (viewName != string.Empty) { return View(viewName,"~/Views/Shared/Clean.master", viewModel); } var v = View(viewModel); v.MasterName ="~/Views/Shared/Clean.Master"; return v; default: throw new FormatException(string.Concat("Cannot server the content in the request format:", RequestedFormat)); } } protected ActionResult FormatView(object viewModel) { return FormatView("", viewModel); } } |
Clean.master只是一个不包含任何其他html的母版页 - 它接受视图(这样我可以合并任何部分类)并使用可以直接放置的干净html呈现它。
如果我想要json - 控制器构建我的viewmodel然后将该视图模型作为json返回,而不是发送到默认视图 - 与.xml相同。
部分视图有点有趣,按照惯例,我的所有主视图都被分解为部分视图,因此可以自己请求特定的部分 - 这可以方便地使用jquery模拟updatepanel的功能而不需要所有垃圾与updatepanel相关联。
至于我,我选择数据驱动的方法。以下是两个小功能:
数据驱动:
加载
HTML的驱动:
因此,即使使用HTML驱动的方法,您也可以维护MVC,尽管它会更难。
为什么不同时使用json和html?
在当前项目中我们正在制作路线,因此您可以从前端进行选择,在某些情况下哪种格式最适合...所以为什么不制作两个控制器,首先将返回json中的预期数据,另一个控制器将返回相同的数据,但与HTML ...这样你可以选择让我们说jQuery什么时候你想要什么,你想要它的格式......最好的是,对于不同的格式你只需要调用不同的地址...
换句话说,让它安静下来,宝贝! :)
干杯
为了保持关注点的分离,您应该返回JSON。
当您返回html时,您将限制使用返回的数据执行的操作。如果您需要一个数据列表并希望以不同的方式呈现它,请使用JSON,否则您必须在服务器上使用不同的方法来获取相同数据的不同呈现。
如果您使用JQuery,您应该使用JSON或XML,因为它更容易修改,但如果您的ajax调用仅返回列表框的项目,例如您也可以使用html。
我的个人偏好是JSON或XML,因为经常使用JQuery