2

我想避免if Request.IsAjaxRequest()在我的控制器中有很多。我在想,如果我可以将这个逻辑浓缩到一个 ActionFilter 中,那么在我的应用程序中采用一个约定来为可能使用 Ajax 的任何请求提供第二个操作,同时在 JavaScript 被禁用时提供一个回退是很容易的。

public ActionResult Details(int id)
{
  // called normally, show full page
}

public ActionResult Details_Ajax(int id)
{
  // called through ajax, return a partial view
}

我最初认为我可以做这样的事情:

public class AjaxRenameAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.RouteData.Values["action"] = filterContext.RouteData.Values["action"] + "_Ajax";

    }

但这不起作用,因为要调用的操作已确定,然后处理其上的过滤器。

我真的不想在每次有人调用动作时都返回一个 RedirectResult ,将 Http 请求的数量翻倍似乎有点毫无意义。

是否有不同的方式将请求路由到不同的操作?或者我正在做的事情是不可取的,我应该寻找一种更好的做事方式?

干杯

4

2 回答 2

2

MvcFutures 中的 AcceptAjaxAttribute 怎么样?

于 2009-12-14T16:14:37.490 回答
1

AcceptAjaxAttribute可能是这里需要的;但是,我想提出一种不同的方式来思考这个问题。

并非所有的 Ajax 请求都是平等的。Ajax 请求可能试图完成以下任何事情:

  • 将 JSON 数据绑定到丰富的网格(如 jqGrid);
  • 解析/转换 XML 数据,例如 RSS 提要;
  • 将部分 HTML 加载到页面的某个区域;
  • 异步加载脚本(google.load可以这样做);
  • 处理来自客户端的单向消息;
  • 可能还有一些我忘记了。

当您仅基于该IsAjaxRequest方法“选择”特定的“替代操作”时,您会将一些非常通用的东西 - 异步请求 - 与服务器上的特定功能联系起来。它最终会让你的设计更脆弱,也让你的控制器更难进行单元测试(虽然有办法做到这一点,你可以模拟上下文)。

一个设计良好的动作应该是一致的,它应该只关心请求是为了什么而不是请求是如何提出的。有人可能会指出其他属性,例如AuthorizeAttribute例外,但我会对过滤器进行区分,大多数情况下,过滤器描述了必须在动作发生“之前”或“之后”发生的行为,而不是“代替”。

说到这里,问题中陈述的目标是一个很好的目标;对于正确描述为不同操作的内容,您绝对应该有不同的方法:

public ActionResult Details(int id)
{
    return View("Details", GetDetails(id));
}

public ActionResult JsonDetails(int id)
{
    return Json(GetDetails(id));
}

public ActionResult PartialDetails(int id)
{
    return PartialView("DetailTable", GetDetails(id));
}

等等。但是,使用 Ajax 操作选择器在这些方法之间进行选择是遵循“优雅降级”的做法,该做法基本上已被渐进增强取代(至少 IMO)。

这就是为什么,虽然我喜欢 ASP.NET MVC,但我大多避开 . AjaxHelper,因为我发现它不能很好地表达这个概念;它试图对你隐藏太多。我们不再使用“Ajax 表单”或“Ajax 操作”的概念,而是消除区别并坚持使用直接 HTML,然后在我们确定客户端可以处理它时单独注入 Ajax 功能。

这是 jQuery 中的一个示例 - 尽管您也可以在 MS AJAX 中执行此操作:

$(function() {
    $("#showdetails").click(function() {
        $("#details").load("PartialDetails", { id: <%= Record.ID %> });
        return false;
    }
});

这就是将 Ajax 注入 MVC 页面所需的全部内容。从一个普通的旧 HTML 链接开始,然后用一个 Ajax 调用覆盖它,该调用转到另一个控制器操作

现在,如果在您网站的其他地方,您决定要使用网格,但又不想使用部分渲染来破坏页面,您可以编写类似这样的内容(假设您有一个主从页面左侧的“订单”列表和右侧的详细信息表):

$(".detaillink").click(function() {
    $('#detailGrid').setGridParam({
        url: $(this).attr("href").replace(/\/order\/details/i,
            "/order/jsondetails")
    }); 
    $("#detailGrid").trigger("reloadGrid");  
});

这种方法将客户端行为与服务器行为完全分离。服务器实际上是在对客户端说:如果您想要 JSON 版本,请询问JSON 版本,哦,顺便说一下,如果您知道如何运行,这里有一个脚本可以转换您的链接。 没有动作选择器和方法重载,没有为了运行简单的测试而必须做的特殊模拟,没有混淆哪个动作在什么时候做什么。只需几行 JavaScript。控制器动作简短而甜蜜,正是它们应有的方式。

这不是唯一的方法。显然,AcceptAjaxAttribute存在诸如此类的类是因为它们希望某些开发人员使用请求检测方法。但是在对两者进行了相当多的试验之后,我发现这种方式容易推理,因此更容易正确设计/编码。

于 2010-01-17T15:44:08.580 回答