ASP.NET MVC:AJAX调用的控制器是应该返回JSON还是渲染html?

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然后让客户端视图排序可能会因为以下限制而变得混乱:

  • 没有JavaScript的标准模板语言。在最糟糕的情况下,您会想要连接字符串以形成您需要的HTML。
  • 没有简单的方法来对您的连接代码生成的HTML进行单元测试。
  • 您的JavaScript缺乏智能感知意味着您也容易犯更多错误。
  • 我处理这个的方法是返回渲染的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相关联。


    至于我,我选择数据驱动的方法。以下是两个小功能:

    数据驱动:

  • 来自服务器的JSON(Controller直接将模型发送到响应)
  • JS模板绑定(可能需要更多时间在客户端)
  • 带宽低
    加载
  • 它太性感了!
  • HTML的驱动:

  • 来自服务器的HTML(Controller将模型发送到某个部分View,它呈现结果并返回它 - 可能需要更多时间在服务器端)
  • JS绑定没有重载
  • 高带宽负载
  • 不性感,没有没有:)
  • 因此,即使使用HTML驱动的方法,您也可以维护MVC,尽管它会更难。


    为什么不同时使用json和html?
    在当前项目中我们正在制作路线,因此您可以从前端进行选择,在某些情况下哪种格式最适合...所以为什么不制作两个控制器,首先将返回json中的预期数据,另一个控制器将返回相同的数据,但与HTML ...这样你可以选择让我们说jQuery什么时候你想要什么,你想要它的格式......最好的是,对于不同的格式你只需要调用不同的地址...

    换句话说,让它安静下来,宝贝! :)

    干杯


    为了保持关注点的分离,您应该返回JSON。

    当您返回html时,您将限制使用返回的数据执行的操作。如果您需要一个数据列表并希望以不同的方式呈现它,请使用JSON,否则您必须在服务器上使用不同的方法来获取相同数据的不同呈现。


    如果您使用JQuery,您应该使用JSON或XML,因为它更容易修改,但如果您的ajax调用仅返回列表框的项目,例如您也可以使用html。

    我的个人偏好是JSON或XML,因为经常使用JQuery