【问题标题】:Using a filter to execute a different action?使用过滤器执行不同的操作?
【发布时间】:2009-12-14 14:36:26
【问题描述】:

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

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

干杯

【问题讨论】:

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


    【解决方案1】:

    MvcFutures 中的 AcceptAjaxAttribute 怎么样?

    【讨论】:

    • 我不想限制一个动作只接受 Ajax 调用,如果它是一个 ajax 请求,我希望动作表现不同,这样如果 javascript 被禁用,花哨的东西就会失败并且它会进行正常的页面发布/刷新
    • 对不起,我误会了。
    • 这实际上是完全 AcceptAjax 所做的。它不是一个动作过滤器,它是一个动作方法选择器。将属性应用于您的 AJAX 方法,并且在您的非 AJAX 方法上不添加任何属性。 ASP.NET MVC 将使用该属性来确定调用这两种方法中的哪一种。在我看来,这正是你想要的。试一试,让我们知道。
    • 啊,我没有意识到,我得研究一下
    • @Eilon 我想这意味着你的两种方法必须有不同的签名?该名称或名称不同,并使用属性来映射其操作名称。
    【解决方案2】:

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

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

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

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

    一个设计良好的动作应该是一致的,它应该只关心请求是为了什么而不是如何 em> 已提出请求。有人可能会指出像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 等类的存在是因为他们希望一些开发人员使用请求检测方法。但在对这两种方法进行了大量试验后,我发现这种方式非常更容易推理,因此更容易正确设计/编码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-14
      • 1970-01-01
      • 1970-01-01
      • 2010-11-23
      • 1970-01-01
      • 1970-01-01
      • 2013-11-26
      相关资源
      最近更新 更多