【问题标题】:MVC ActionFilter : Rewrite request / redirect to ActionMVC ActionFilter:重写请求/重定向到动作
【发布时间】:2015-10-26 15:26:42
【问题描述】:

想象一个站点绑定了许多域。 f.e. product1.com、product2.com、...

当 product23 出现故障时,想法是让“landingpagecontroller”处理对 product23.com 的所有请求。

这个想法是写一个 ActionFilter 来完成这个:

   public class Landingpage : IActionFilter
{

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var DnsSafeHost = filterContext.HttpContext.Request.Url.DnsSafeHost;
        if (DnsSafeHost.NeedsLandingpage())
        {
            //Do actual redirecting
         }
    }
}

NeedsLandingpage() 返回一个布尔值。如果需要登陆页面控制器服务,则为真。情报将在数据库中找到。

我已经添加了到登录页面控制器的路由。

            routes.MapRoute(
            name: "Ladingpage",
            url: "Landingpage/{id}/{*dummy}",
            defaults: new { controller = "LandingPage", action = "Index" }
        );

关于如何从 actionfilter 更改路由设置以触发上述路由或实现目标的更好解决方案的任何提示。

********** 已更新 ************

鉴于大家的意见,我想出了以下可行的解决方案,但我对它并不是 100% 满意。因此,欢迎提供更好的解决方案。

我创建了以下 IActionFilter 并将其注册为全局。

public class Landingpage : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var DnsSafeHost = filterContext.HttpContext.Request.Url.DnsSafeHost;
            var _LandingPage = LandingPage(DnsSafeHost);
            if (_LandingPage != null)
            {
                if ((String)filterContext.RouteData.Values["controller"] != "Landingpage")
                {
                    filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Landingpage", action = "Index", id = _LandingPage }));
                    filterContext.Result.ExecuteResult(filterContext.Controller.ControllerContext);
                }
            }
        }
        private String LandingPage(String DnsSafeHost)
        {
            if (DnsSafeHost.Contains("<domain to look for>".ToLower())) return "<viewname>";
            if (DnsSafeHost.Contains("<domain to look for>".ToLower())) return "<viewname>";
            if (DnsSafeHost.Contains("<domain to look for>".ToLower())) return "<viewname>";
            return null;
        }
    }

我不喜欢的解决方案是检查 (String)filterContext.RouteData.Values["controller"]

这是需要的,否则你会陷入循环,因为。

浏览器 -> 路由 -> 过滤器 -> 重定向 -> 路由 -> 过滤器 ...

我认为可能的解决方案是在 Application_BeginRequest (global.asax) 中做一些事情。但我没有找到“重写”请求的方法。因为如果可能的话,流程将是: 浏览器 -> BeginRequest -> RewriteRequest -> 路由 -> 控制器。

我猜,如果您遵循最佳实践,那么在 vNext 中,这似乎不再适用。

Filterconfig.cs

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new Landingpage());

    }
}

所有“登陆页面”调用都被重定向到登陆页面控制器。 调用 ViewEngine 是为了检查从 actionfilter 传递的视图名称是否存在,如果不使用默认值。

   public class LandingpageController : Controller
    {
        public ActionResult Index(string id)
        {
            if (id == null) return View();
            if (ViewEngines.Engines.FindView(ControllerContext, id, null).View == null) return View();
            ViewBag.Title = id;
            return View(id);
        }
    }

视图示例。

@{
    Layout = "_Index_layout.cshtml";
}

<div class="domainname">Landingpage</div>

为了给它一个不同于普通页面的“特殊”布局(例如没有菜单),我为此页面添加了一个自定义布局。

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <style>
--- add some styling here ---
     </style>
</head>

<body>
    @RenderBody()
</body>

</html>

【问题讨论】:

  • 可以在这里找到可能的解决方案(特别是Syakur Ra​​hman给出的答案)http://stackoverflow.com/questions/5453338/redirect-from-action-filter-attribute
  • 在发布 Q 之前尝试过。它循环多次并退出循环,但从未真正进入控制方法。问题是这是一个重定向,因此一次又一次地执行这个过滤器。我正在寻找一种方法来更改请求,以便通过路由引擎的请求与在浏览器中输入 www.product21.com/landingpage 的请求相同。
  • 也许我搞错了。路由是在全局过滤器之前还是之后?

标签: c# asp.net-mvc custom-action-filter


【解决方案1】:

我查看了一些我过去用来做类似事情的代码。基本上我是这样做的:

首先,我创建了一个基于RedirectToRouteResult 的类(你不必这样做,但我放在这里是为了说明)。

namespace Blah
{
    using System;
    using System.Web.Mvc;
    using System.Web.Routing;

    public class SessionTimeoutRedirectResult : RedirectToRouteResult
    {
        public SessionTimeoutRedirectResult(
            string routeName,
            RouteValueDictionary routeValueDictionary,
            string redirectActionName,
            string redirectControllerName)
            : base(routeName, routeValueDictionary)
        {
            RedirectActionName = redirectActionName;
            RedirectControllerName = redirectControllerName;
        }

        public string RedirectActionName { get; private set; }

        public string RedirectControllerName { get; private set; }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            RouteValues.Clear();
            RouteValues.Add("action", RedirectActionName);
            RouteValues.Add("controller", RedirectControllerName);

            base.ExecuteResult(context);
        }
    }
}

然后在操作过滤器中,我的代码执行了以下操作:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var controller = filterContext.Controller as BaseController;

    if (controller != null)
    {
        if (controller.Session.IsNewSession)
        {
            var routeDictionary = filterContext.RouteData.DataTokens;

            filterContext.Result = new SessionTimeoutRedirectResult(
                "Error500",
                routeDictionary,
                RedirectActionName,
                RedirectControllerName);
        }
    }

    base.OnActionExecuting(filterContext);
}

因此,我的操作过滤器将根据会话重定向路由结果重定向用户。同样,您不必创建基于 RedirectToRouteResult 的类,但出于某种原因我当时这样做了。

最终的关键是设置filterContext.Result,因为这将重定向用户。

【讨论】:

    【解决方案2】:

    正如我在编辑中提到的,我有一个解决方案,但由于重定向和摆脱重定向循环的肮脏方式,我并不是 100% 满意。

    如果你看一下请求处理管道。

    http://saulius.sunauskas.com/wp-content/uploads/asp_net__mvc_request_pipeline_4.png

    更改请求的地方是流程图上的“IRouteHandler”。

    在这里找到了一个很好的创建自定义示例:

    http://www.brainthud.com/cards/5218/24821/give-an-example-of-how-you-would-create-a-custom-route-handler-to-reroute-requests-with-a-certain-value-in-the-accept-http-he

    最终解决方案的原型是:

    1. 从 MVCRouteHandler 创建一个自定义 IRouteHandler :

      public class LandingPageRouteHandler : MvcRouteHandler
      {
          protected override IHttpHandler GetHttpHandler(RequestContext Context)
          {
              if (  Context.HttpContext.Request.Url.DnsSafeHost.ToLower().Contains("onecityguide"))
              {
                  Context.RouteData.Values["controller"] = "LandingPage";
                  Context.RouteData.Values["action"] = "Index"; 
                  Context.RouteData.Values["id"] = "onecityguide";
              }
              return base.GetHttpHandler(Context);
          }
      }
      
    2. 将自定义 RouteHandler 连接到默认路由。

      routes.MapRoute(
          name: "Default",
          url: "{controller}/{action}/{id}",
          defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
      ).RouteHandler = new LandingPageRouteHandler();
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-14
      相关资源
      最近更新 更多