c# - iactionfilter - ¿Cómo pasar parámetros a un ActionFilter personalizado en ASP.NET MVC 2?
net core exception filter (4)
Esta es una manera de hacer que esto funcione. Tiene acceso a ControllerContext y, por lo tanto, a Controller desde el objeto ActionFilter. Todo lo que necesita hacer es convertir su controlador al tipo y puede acceder a cualquier miembro público.
Dado este controlador:
public GenesisController : Controller
{
[CheckLoggedIn()]
public ActionResult Home(MemberData md)
{
return View(md);
}
}
ActionFilter se ve algo así como
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);
}
}
Por supuesto, esto es asumiendo que el ActionFilter no se usa en varios controladores y está bien con el acoplamiento. Otra opción es hacer una interfaz ICheckedLoggedInController con las propiedades compartidas y simplemente convertir a eso en su lugar.
Estoy intentando crear un ActionFilter personalizado que funcione con un conjunto de parámetros que se le pasarían desde el controlador.
Hasta ahora, mi cliente ActionFilter tiene este aspecto:
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);
}
}
Sé que todavía tengo que comprobar si hay nulos, etc., pero no puedo entender por qué gr
y memberGuid
no se pasan con éxito. Estoy llamando a este filtro de esta manera:
[CheckLoggedIn(gr = genesisRepository, memberGuid = md.memberGUID)]
public ActionResult Home(MemberData md)
{
return View(md);
}
genesisRepository
y md
se configuran en el constructor del controlador.
No puedo conseguir esto para compilar. El error que recibo es:
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
Comprobé dos memberGuid
que gr
y memberGuid
eran del mismo tipo que genesisRepority
y md.memberGUID
. ¿Qué está causando estos errores?
Solución
Gracias a jfar por ofrecer una solución.
Aquí está el filtro que terminé usando:
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);
}
}
La inyección de dependencia es la solución ideal en este caso; la ubicación del servicio es otra opción (más simple) a considerar también.
Bueno ... no estoy seguro de que esto sea posible entonces. md.memberGUID está vinculado a la sesión y no puede ser una constante porque el sitio necesitará acceso para actualizar los datos de inicio de sesión.
porque no quiero crear una dependencia para una clave de sesión específica. Preferiría que la comprobación de global.aspx.cs revele el enlace y que un futuro desarrollador pueda realizar cambios fácilmente
Agregar cosas a los datos de ruta que no están en la URL es una forma bastante horrible de almacenar datos. Eso lleva a encontrar errores difíciles de encontrar. - La inyección de dependencia realmente no ayuda a resolver este problema. Cualquier técnica que proporcione los repositorios al ActionFilter funcionará. Ahora, por supuesto, DI es mejor para ayudar a detener los problemas de ubicación del servicio, pero no es LA solución, solo una forma de habilitar la solución. Buen artículo sobre cómo lograr esto: lostechies.com/blogs/jimmy_bogard/archive/2010/05/03/…
Los atributos son esencialmente metadatos agregados a un tipo. Solo pueden usar valores const
, en lugar de variables de instancia. En su caso, está genisisRepository
pasar las variables de instancia de genisisRepository
, etc. Esto no podrá compilarse ya que no son constantes de tiempo de compilación.
Para lograr esto, debe buscar en Inyección de dependencias para filtros de acción, por lo general, utilizando un contenedor IoC.
Además, si su ActionFilter está realizando una acción posterior a ActionResult, como OnActionExecuted
, es probable que pueda guardar algo en los datos de la ruta:
public ActionResult Index()
{
ControllerContext.RouteData.DataTokens.Add("name", "value");
return View();
}
Solo puedes usar valores constantes para propiedades de atributos; ver a esta página para una explicación completa.