MVC ActionFilter:重写请求/重定向到Action

时间:2015-10-26 15:26:42

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

想象一个网站,它有许多与之绑定的域名。 F.E. product1.com,product2.com,...

当product23出现故障时,我们的想法就是拥有一个“登陆控制器”'满足对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()返回一个布尔值。如果需要由登陆控制器进行维修,则为True。情报将在数据库中找到。

我已经添加了一个到目标网页控制器的路由。

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

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

**********更新************

鉴于您的意见,我提出了以下工作解决方案,但我并不是百分之百满意。所以欢迎更好的解决方案。

我创建了以下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 [&#34; controller&#34;]

这是需要的,或者你陷入了循环,因为。

浏览器 - &gt;路由 - &gt;过滤器 - &gt;重定向 - &gt;路由 - &gt;过滤......

我认为可能的解决方案是在Application_BeginRequest(global.asax)中执行某些操作。但是我没有办法重写&#39;请求。因为如果可能的话,流程将是: 浏览器 - &gt; BeginRequest - &gt; RewriteRequest - &gt;路由 - &gt;控制器。

如果你遵循最佳实践,那么在vNext中这样的内容似乎就不再适用了。

Filterconfig.cs

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

    }
}

所有&#39;登陆&#39;调用被重定向到LandingPageController。 对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>

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

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

<body>
    @RenderBody()
</body>

</html>

2 个答案:

答案 0 :(得分: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,因为这会重定向用户。

答案 1 :(得分:0)

正如我在编辑中所提到的,我有一个解决方案但由于重定向和肮脏的方式退出重定向循环而不是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();