【问题标题】:How to pass parameters to a custom ActionFilter in ASP.NET MVC 2?如何将参数传递给 ASP.NET MVC 2 中的自定义 ActionFilter?
【发布时间】:2011-05-19 21:12:50
【问题描述】:

我正在尝试创建一个自定义 ActionFilter,它对一组从控制器传递给它的参数进行操作。

到目前为止,我的客户 ActionFilter 如下所示:

public class CheckLoggedIn : ActionFilterAttribute
{
    public IGenesisRepository gr { get; set; }
    public Guid memberGuid { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Member thisMember = gr.GetActiveMember(memberGuid);
        Member bottomMember = gr.GetMemberOnBottom();

        if (thisMember.Role.Tier <= bottomMember.Role.Tier)
        {
            filterContext
                .HttpContext
                .Response
                .RedirectToRoute(new { controller = "Member", action = "Login" });
        }

        base.OnActionExecuting(filterContext);
    }
}

我知道我仍然需要检查空值等,但我无法弄清楚为什么 grmemberGuid 没有成功通过。我这样称呼这个过滤器:

    [CheckLoggedIn(gr = genesisRepository, memberGuid = md.memberGUID)]
    public ActionResult Home(MemberData md)
    {
        return View(md);
    }

genesisRepositorymd 正在控制器的构造函数中设置。

我无法编译它。我得到的错误是:

Error   1   'gr' is not a valid named attribute argument because it is not a valid attribute parameter type
Error   2   'memberGuid' is not a valid named attribute argument because it is not a valid attribute parameter type

我仔细检查了grmemberGuidgenesisReporitymd.memberGUID 的类型相同,是什么导致了这些错误?

解决方案

感谢 jfar 提供解决方案。

这是我最终使用的过滤器:

public class CheckLoggedIn : ActionFilterAttribute
{

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var thisController = ((MemberController)filterContext.Controller);

        IGenesisRepository gr = thisController.GenesisRepository;
        Guid memberGuid = ((MemberData)filterContext.HttpContext.Session[thisController.MemberKey]).MemberGUID;

        Member thisMember = gr.GetActiveMember(memberGuid);
        Member bottomMember = gr.GetMemberOnBottom();

        if (thisMember.Role.Tier >= bottomMember.Role.Tier)
        {
            filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary(
                    new { 
                        controller = "Member", 
                        action = "Login" 
                    }));
        }

        base.OnActionExecuting(filterContext);
    }
}

【问题讨论】:

  • 您的问题非常棒,但解决方案比问题更棒。我正在寻找一种将参数发送到全局过滤器属性的解决方案。这对我来说就像止痛药:)

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


【解决方案1】:

这是实现这项工作的一种方法。您可以从 ActionFilter 对象访问 ControllerContext 并因此访问 Controller。您需要做的就是将您的控制器转换为该类型,然后您就可以访问任何公共成员。

给定这个控制器:

public GenesisController : Controller
{
    [CheckLoggedIn()]
    public ActionResult Home(MemberData md)
    {
        return View(md);
    }
}

ActionFilter 看起来像

public class CheckLoggedIn : ActionFilterAttribute
{
    public IGenesisRepository gr { get; set; }
    public Guid memberGuid { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        /* how to get the controller*/
        var controllerUsingThisAttribute = ((GenesisController)filterContext.Controller);

        /* now you can use the public properties from the controller */
        gr = controllerUsingThisAttribute .genesisRepository;
        memberGuid = (controllerUsingThisAttribute .memberGuid;

        Member thisMember = gr.GetActiveMember(memberGuid);
        Member bottomMember = gr.GetMemberOnBottom();

        if (thisMember.Role.Tier <= bottomMember.Role.Tier)
        {
            filterContext
                .HttpContext
                .Response
                .RedirectToRoute(new { controller = "Member", action = "Login" });
        }

        base.OnActionExecuting(filterContext);
    }
}

当然,这是假设 ActionFilter 没有跨多个控制器使用,并且您对耦合没问题。另一种选择是使用共享属性创建一个 ICheckedLoggedInController 接口,然后简单地转换为该接口。

【讨论】:

  • 嗯,这让我更接近解决方案。谢谢jfar。不过,actionFilter 将在多个控制器中使用。
  • 我在获取控制器时遇到了很多麻烦。var thisController = ((MemberController)ControllerContext.Controller); 返回错误:An object reference is required for the non-static field, method, or property 'System.Web.Mvc.ControllerContext.Controller.get'
  • @quakkels - 哎呀,这不是我的想法。您可以通过filterContext.Controller 获得控制器。也更新答案。
  • [CheckLoggedIn(gr = genesisRepository, memberGuid = md.memberGUID)] ???这个怎么运作?正如@DanP 所说,您只能对属性属性使用常量值。
  • 我也注意到了这一点。 18 票赞成无法编译的答案。我想知道这是否是一个记录。
【解决方案2】:

属性属性只能使用常量值;请参阅this page 了解完整说明。

【讨论】:

    【解决方案3】:

    属性本质上是添加到类型的元数据。他们只能使用const 值,而不是实例变量。在您的情况下,您要传入 genisisRepository 等实例变量。这将无法编译,因为它们不是编译时常量。

    您应该研究动作过滤器的依赖注入来实现这一点,通常使用 IoC 容器。

    此外,如果您的 ActionFilter 正在执行 post ActionResult 操作,例如 OnActionExecuted,您可能会在路由数据中存储一些内容:

    public ActionResult Index()
    {
      ControllerContext.RouteData.DataTokens.Add("name", "value");
      return View();
    }
    

    【讨论】:

    • +1 用于提供进一步的指导......在这种情况下,依赖注入是理想的解决方案;服务地点也是另一个需要考虑的(更简单的)选项。
    • 嗯...我不确定这是否可能。 md.memberGUID 绑定到会话并且不能是 const,因为该站点需要访问权限才能更新登录数据。
    • 因为我不想创建对特定会话密钥的依赖关系。我宁愿检查 global.aspx.cs 会显示绑定,并且未来的开发人员可以轻松地进行更改
    • 将不在 URL 中的内容添加到路由数据中是一种非常糟糕的数据存储方式。这导致很难找到错误。 -- 依赖注入并不能真正帮助解决这个问题。任何向 ActionFilter 提供存储库的技术都可以使用。现在当然 DI 可以更好地帮助解决服务位置问题,但它不是解决方案,只是启用解决方案的一种方式。关于如何实现这一点的好文章:lostechies.com/blogs/jimmy_bogard/archive/2010/05/03/…
    • 你不需要 IoC 来注入它,但 IoC 可能会让它更容易。您可以在过滤器配置中声明过滤器,将其依赖项注入构造函数,然后注册过滤器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-07
    • 2017-07-30
    • 2021-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多