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

发布于 2024-08-14 21:23:03 字数 854 浏览 4 评论 0原文

我想避免在我的控制器中出现大量 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 请求量加倍似乎有点毫无意义。

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

干杯

I want to avoid having lots of if Request.IsAjaxRequest() in my controllers. I was thinking that if I could condense this logic to an ActionFilter it would be easy to adopt a convention in my application to provide a second action for any request that may use Ajax, while providing a fall back if JavaScript is disabled.

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

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

I initially thought I could do something like this:

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

    }

But that won't work because the action to invoke is decided and then the Filters on it are processed.

I don't really want to return a RedirectResult each time someone calls an action, it seems a bit pointless to double the amount of Http requests.

Is there a different way to route through the request to a different action? Or is what I'm doing unadvisable and I should look for a better way of doing things?

Cheers

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

╰沐子 2024-08-21 21:23:03

MvcFutures 中的 AcceptAjaxAttribute 怎么样?

How about AcceptAjaxAttribute in MvcFutures?

七婞 2024-08-21 21:23:03

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

并非所有 Ajax 请求都是相同的。 Ajax 请求可能会尝试完成以下任何事情:

  • 将 JSON 数据绑定到丰富的网格(如 jqGrid);
  • 解析/转换 XML 数据,例如 RSS feed;
  • 将部分 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 操作选择器在这些方法之间进行选择是遵循“优雅降级”的做法,它基本上已被渐进增强取代(至少在我看来)。

这就是为什么,尽管我喜欢 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 等类的存在是因为它们希望某些开发人员使用请求检测方法。但在对两者进行了大量实验之后,我发现这种方式更容易推理,因此更容易正确设计/编码。

AcceptAjaxAttribute is probably is what's needed here; however, I'd like to propose a different way of thinking about this problem.

Not all Ajax requests are equal. An Ajax request could be trying to accomplish any of the following things:

  • Binding JSON data to a rich grid (like jqGrid);
  • Parsing/transforming XML data, such as an RSS feed;
  • Loading partial HTML into an area of the page;
  • Asynchronously loading a script (google.load can do this);
  • Handling a one-way message from the client;
  • And probably a few more that I'm forgetting.

When you "select" a specific "alternate action" based solely on the IsAjaxRequest method, you are tying something very generic - an asynchronous request - to specific functionality on the server. It's ultimately going to make your design more brittle and also make your controller harder to unit test (although there are ways to do it, you can mock the context).

A well-designed action should be consistent, it should only care what the request was for and not how the request was made. One might point to other attributes like AuthorizeAttribute as exceptions, but I would make a distinction for filters, which most of the time describe behaviour that has to happen either a "before" or "after" the action takes place, not "instead of."

Getting to the point here, the goal stated in the question is a good one; you should definitely have different methods for what are correctly described as different actions:

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));
}

And so on. However, using an Ajax action selector to choose between these methods is following the practice of "graceful degradation", which has essentially been superseded (at least IMO) by progressive enhancement.

This is why, although I love ASP.NET MVC, I mostly eschew the AjaxHelper, because I don't find that it expresses this concept that well; it tries to hide too much from you. Instead of having the concept of an "Ajax Form" or an "Ajax Action", let's do away with the distinction and stick to straight HTML, then inject the Ajax functionality separately once we're certain that the client can handle it.

Here's an example in jQuery - although you can do this in MS AJAX too:

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

This is all it takes to inject Ajax into an MVC page. Start with a plain old HTML link and override it with an Ajax call that goes to a different controller action.

Now, if on somewhere else on your site you decide you want to use a grid instead, but don't want to break the pages using partial rendering, you can write something like this (let's say you have a single master-detail page with a list of "orders" on the left side and a detail table on the right):

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

This approach completely decouples client behaviour from server behaviour. The server is effectively saying to the client: If you want the JSON version, ask for the JSON version, and oh, by the way, here's a script to convert your links if you know how to run it. No action selectors and finagling with method overloads, no special mocking you have to do in order to run a simple test, no confusion over which action does what and when. Just a couple of lines of JavaScript. The controller actions are short and sweet, exactly the way they should be.

This is not the only approach. Obviously, classes such as AcceptAjaxAttribute exist because they expected some developers to use the request-detection method. But after having experimented quite a bit with both, I find this way much easier to reason about and therefore easier to design/code correctly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文