我在纠结一个由AJAX调用的控制器操作应该返回一个部分视图还是“原始”JSON。
返回一个已呈现HTML的部分视图使得JavaScript可以更轻松地使用返回的HTML更新当前DOM。然而,它限制了JavaScript客户端对使用Web服务返回的HTML所能做的事情。
另一方面,控制器操作返回JSON则要求调用的JavaScript基于返回的JSON“手动”创建标记。
因此,像往常一样,每种方法都有其优点和缺点。每种方法还有其他的优缺点吗?
我在纠结一个由AJAX调用的控制器操作应该返回一个部分视图还是“原始”JSON。
返回一个已呈现HTML的部分视图使得JavaScript可以更轻松地使用返回的HTML更新当前DOM。然而,它限制了JavaScript客户端对使用Web服务返回的HTML所能做的事情。
另一方面,控制器操作返回JSON则要求调用的JavaScript基于返回的JSON“手动”创建标记。
因此,像往常一样,每种方法都有其优点和缺点。每种方法还有其他的优缺点吗?
FilterRequests: function() {
$.post("/Request.aspx/GetFilteredRequests", { }, function(data) {
$('ul.requests').html(data);
});
},
这是我的控制器上的操作:
public ActionResult GetFilteredRequests(string filterJson)
{
var requests = _requestDao.LoadAll();
return PartialView("FilteredRequests", requests);
}
最后这是我的部分视图(没必要理解这个,我只是想展示一下在现实世界应用中渲染可以变得多么复杂。如果用JavaScript来做这件事,我会感到害怕的。你还会注意到,我的部分视图反过来也调用了其他部分视图。):
<%@ 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">
<h2>#<%= request.Id %>: <%= request.InitialInteraction().Description %></h2>
<p>from <%= request.Customer.FullName %> (<%= request.Customer.EmailAddress %>), <%= request.InitialInteraction().UsableTimeStamp %></p>
<h3>Custom Fields & Lookups</h3>
<div class="tabs">
<ul>
<li><a href="#customfields<%= request.Id %>">Custom Fields</a></li>
<% foreach (var lookupDefinition in (List<LookupDefinition>)ViewData["LookupDefinitions"]) %>
<%{%>
<li><a href="#<%= lookupDefinition.Name.ToLowerInvariant().Replace(" ", "") + request.Id %>"><%= lookupDefinition.Name %></a></li>
<%}%>
</ul>
<% Html.RenderPartial("CustomFields", request); %>
</div>
<% Html.RenderPartial("Discussion", request); %>
<% Html.RenderPartial("Attachment", request); %>
</li>
<%}%>
我希望让调用应用程序来决定。我已经组合了一个MultiViewController(其中大部分代码是我在网上找到的,当我找到时,我会尽力更新它的来源),根据操作扩展名,将返回适当的格式。例如:
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);",我只需调用"return FormatView(Model);"或者"FormatView("ViewName",Model);"。如果需要将特定的视图应用于结果而不是暗示的视图,则使用后者。
MultiViewController的代码如下。请特别注意FormatView,它返回一个操作结果:
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 - 控制器构建我的视图模型,然后将该视图模型作为json返回,而不是发送到默认视图 - .xml也是同样的情况。
部分视图有点有趣,按照惯例,所有的主视图都被拆分成部分视图,这样特定的部分视图就可以单独请求 - 这对于使用jquery模仿updatepanel的功能而不带有与updatepanel相关的所有垃圾非常方便。