如果请求源自 AJAX 调用,如何有条件地路由到某个操作?

发布于 2025-01-07 10:58:21 字数 304 浏览 3 评论 0原文

使用 APS.NET 路由,如何根据请求是否源自 AJAX 调用有条件地路由到单独的操作?

例如,在控制器上,我可能有两个操作:

public ActionResult List() { return View(); }

并且

public ActionResult ListJSON() { return Content(...); }

我希望这两个操作具有相同的 URL,但如果请求源自 AJAX 调用,则应该调用 ListJSON()。

Using APS.NET routing, how do you conditionally route to separate actions based on whether or not the request originated as an AJAX call?

For instance on a controller I may have two actions:

public ActionResult List() { return View(); }

and

public ActionResult ListJSON() { return Content(...); }

I'd like both actions to have the same URL, but ListJSON() should get called if the request originated as an AJAX call.

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

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

发布评论

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

评论(2

白龙吟 2025-01-14 10:58:21

当代码相同时为什么要使用 2 个单独的操作?这是视图结果的不同。

怎么样:

public ActionResult List()
{
    var model = ...
    if (Request.IsAjaxRequest())
    {
        return View(model);
    }
    return Json(model);
}

显然,如果你必须在每个控制器操作中都这样做,那么这很快就会变成一场彻底的噩梦。

因此,您可以将此逻辑外部化为自定义操作过滤器:

[MyFilter]
public ActionResult List()
{
    var model = ...
    return View(model);
}

您可以在其中定义 MyFilterAttribute ,如下所示:

public class MyFilterAttribute: ActionFilterAttribute
{
     public override void OnResultExecuted(ResultExecutedContext filterContext)
     {
         if (filterContext.HttpContext.Request.IsAjaxRequest())   
         {
             var result = filterContext.Result as ViewResultBase;
             if (result != null)
             {
                 filterContext.Result = new JsonResult
                 {
                     Data = result.Model,
                     JsonRequestBehavior = JsonRequestBehavior.AllowGet
                 };
             }
         }
     }
}

您还可以将此操作过滤器注册为全局操作过滤器,以避免将其放在每个控制器上以及需要它的行动。


更新:

正如评论部分所解释的,OP 似乎需要 2 个不同的操作。为此,您可以使用自定义路由:

public class MyRoute : Route
{
    public MyRoute(string url, object defaults) :
        base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
    {
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var rd = base.GetRouteData(httpContext);
        if (rd == null)
        {
            return null;
        }

        if (httpContext.Request.IsAjaxRequest())
        {
            rd.Values["action"] = rd.GetRequiredString("action") + "json";
        }

        return rd;
    }
}

它将在 Application_Start 中注册:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.Add(
        "Default",
        new MyRoute(
            "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        )
    );
}

Why using 2 separate actions when the code is the same? It's the view result that differs.

How about:

public ActionResult List()
{
    var model = ...
    if (Request.IsAjaxRequest())
    {
        return View(model);
    }
    return Json(model);
}

Obviously if you had to do this in every controller action this would quickly become a complete nightmare.

So you could externalize this logic into a custom action filter:

[MyFilter]
public ActionResult List()
{
    var model = ...
    return View(model);
}

where you could define the MyFilterAttribute like so:

public class MyFilterAttribute: ActionFilterAttribute
{
     public override void OnResultExecuted(ResultExecutedContext filterContext)
     {
         if (filterContext.HttpContext.Request.IsAjaxRequest())   
         {
             var result = filterContext.Result as ViewResultBase;
             if (result != null)
             {
                 filterContext.Result = new JsonResult
                 {
                     Data = result.Model,
                     JsonRequestBehavior = JsonRequestBehavior.AllowGet
                 };
             }
         }
     }
}

You could also register this action filter as a global action filter to avoid the need of putting it on each controller and action that requires it.


UPDATE:

As explained in the comments section it seems that the OP requires 2 different actions. For this purpose you could use a custom route:

public class MyRoute : Route
{
    public MyRoute(string url, object defaults) :
        base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
    {
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var rd = base.GetRouteData(httpContext);
        if (rd == null)
        {
            return null;
        }

        if (httpContext.Request.IsAjaxRequest())
        {
            rd.Values["action"] = rd.GetRequiredString("action") + "json";
        }

        return rd;
    }
}

which will be registered in Application_Start:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.Add(
        "Default",
        new MyRoute(
            "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        )
    );
}
只是偏爱你 2025-01-14 10:58:21

事实证明,您可以通过实现IRouteConstraint轻松定义自己的路由约束。

像这样:

public class AjaxRouteConstraint : IRouteConstraint {
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
        return httpContext.Request.IsAjaxRequest();
    }
}

然后只需在路线上使用此约束即可。技巧是带有此约束的路由必须位于没有约束的路由之前,否则ajax路由将永远不会被调用(非ajax路由将始终匹配)。

当涉及到 URL 生成时,这样做可能会产生一些后果。但我还没有对这些进行重大测试。

It turns out that you can easily define your own route constraints by implementing IRouteConstraint.

Something like this:

public class AjaxRouteConstraint : IRouteConstraint {
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
        return httpContext.Request.IsAjaxRequest();
    }
}

Then simply use this constraint on a route. The trick is that the route with this constraint must come before the route without the constraint, otherwise the ajax route will never be called (the not-ajax route will always match).

There may be some consequences to doing this when it comes to URL generation. But I have not significantly tested those.

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