使用过滤器执行不同的操作?

时间:2009-12-14 14:36:26

标签: asp.net-mvc ajax controller-action action-filter

我想避免在我的控制器中有很多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请求的数量增加一倍似乎没有意义。

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

干杯

2 个答案:

答案 0 :(得分:2)

MvcFutures中的AcceptAjaxAttribute怎么样?

答案 1 :(得分: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 Action”的概念,而是取消区别并坚持直接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之类的类存在是因为他们希望一些开发人员使用请求检测方法。但是在对两者进行了相当多的实验之后,我发现很多更容易推理,因此更容易设计/编码。