Redireccionar desde el atributo de filtro de acción

139

¿Cuál es la mejor manera de hacer una redirección en un ActionFilterAttribute. Tengo un ActionFilterAttributellamado IsAuthenticatedAttributeFiltery que verificó el valor de una variable de sesión. Si la variable es falsa, quiero que la aplicación redirija a la página de inicio de sesión. Preferiría redirigir utilizando el nombre de la ruta, SystemLoginsin embargo, cualquier método de redireccionamiento en este punto estaría bien.

ryanzec
fuente

Respuestas:

187

Establecer filterContext.Result

Con el nombre de la ruta:

filterContext.Result = new RedirectToRouteResult("SystemLogin", routeValues);

También puedes hacer algo como:

filterContext.Result = new ViewResult
{
    ViewName = SharedViews.SessionLost,
    ViewData = filterContext.Controller.ViewData
};

Si quieres usar RedirectToAction:

Se podría hacer público un RedirectToActionmétodo en el controlador ( preferiblemente en su controlador de base ) que simplemente llama al protegido RedirectToActionde System.Web.Mvc.Controller. Agregar este método permite una llamada pública a usted RedirectToAction desde el filtro.

public new RedirectToRouteResult RedirectToAction(string action, string controller)
{
    return base.RedirectToAction(action, controller);
}

Entonces su filtro se vería así:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var controller = (SomeControllerBase) filterContext.Controller;
    filterContext.Result = controller.RedirectToAction("index", "home");
}
CRice
fuente
8
Esto funciona, pero ¿no debería haber un método RedirectToAction disponible?
Ben Mills
@BenMills existe, sin embargo, es protectedpara que no tengas acceso desde el filtro.
James
10
Mi pregunta ahora es por qué Microsoft decidió hacer este filtro protected, debe haber alguna explicación razonable. Me siento muy sucio redefiniendo esta accesibilidad RedirectToActionsin entender por qué se encapsuló en primer lugar.
Matthew Marlin
2
@MatthewMarlin: vea la respuesta de Syakur para obtener la respuesta correcta para redirigir a una acción. Tiene razón en que no debería llamar a un controlador directamente desde un filtro de acción, esa es la definición de acoplamiento estrecho.
NightOwl888
1
@ Akbari, ¿ha intentado establecer la propiedad Order de los atributos? FilterScope también impactará en la orden de ejecución.
CRice el
79

Alternativamente a una redirección, si está llamando a su propio código, puede usar esto:

actionContext.Result = new RedirectToRouteResult(
    new RouteValueDictionary(new { controller = "Home", action = "Error" })
);

actionContext.Result.ExecuteResult(actionContext.Controller.ControllerContext);

No es una redirección pura, pero ofrece un resultado similar sin una sobrecarga innecesaria.

Syakur Rahman
fuente
Me ayudaste. ¡Gracias!
Edgar Salazar
25
Tenga en cuenta que no debe llamar actionContext.Result.ExecuteResultdesde su filtro de acción: MVC lo hará automáticamente después de que se ejecute el filtro de acción (siempre actionContext.Resultque no sea nulo).
NightOwl888
12

Estoy usando MVC4, utilicé el siguiente enfoque para redirigir una pantalla html personalizada en caso de incumplimiento de la autorización.

Extender AuthorizeAttributedecir CutomAuthorizer anular el OnAuthorizationyHandleUnauthorizedRequest

Registre el CustomAuthorizeren el RegisterGlobalFilters.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{

    filters.Add(new CustomAuthorizer());
}

al identificar la unAuthorizedllamada de acceso HandleUnauthorizedRequesty redirigir a la acción del controlador en cuestión como se muestra a continuación.


public class CustomAuthorizer : AuthorizeAttribute
{

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        bool isAuthorized = IsAuthorized(filterContext); // check authorization
        base.OnAuthorization(filterContext);
        if (!isAuthorized && !filterContext.ActionDescriptor.ActionName.Equals("Unauthorized", StringComparison.InvariantCultureIgnoreCase)
            && !filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.Equals("LogOn", StringComparison.InvariantCultureIgnoreCase))
        {

            HandleUnauthorizedRequest(filterContext);

        }
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.Result =
       new RedirectToRouteResult(
           new RouteValueDictionary{{ "controller", "LogOn" },
                                          { "action", "Unauthorized" }

                                         });

    }
}
usuario2834076
fuente
9

Parece que usted desea volver a poner en práctica, o posiblemente extender, AuthorizeAttribute. Si es así, debe asegurarse de heredar eso, y no ActionFilterAttribute, para que ASP.NET MVC haga más del trabajo por usted.

Además, debe asegurarse de autorizar antes de realizar cualquier trabajo real en el método de acción; de lo contrario, la única diferencia entre iniciar sesión y no será qué página verá cuando termine el trabajo.

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        // Do whatever checking you need here

        // If you want the base check as well (against users/roles) call
        base.OnAuthorization(filterContext);
    }
}

Hay una buena pregunta con una respuesta con más detalles aquí sobre SO.

Tomás Aschan
fuente
5

Pruebe el siguiente fragmento, debe quedar bastante claro:

public class AuthorizeActionFilterAttribute : ActionFilterAttribute
{
  public override void OnActionExecuting(FilterExecutingContext filterContext)
  {
    HttpSessionStateBase session = filterContext.HttpContext.Session;
    Controller controller = filterContext.Controller as Controller;

    if (controller != null)
    {
      if (session["Login"] == null)
      {
        filterContext.Cancel = true;
        controller.HttpContext.Response.Redirect("./Login");
      }
    }

    base.OnActionExecuting(filterContext);
  }
}
Muhammad Soliman
fuente
Esto funcionó para mí, tuve que verificar los valores de la cadena de consulta si algún usuario intenta cambiar los valores de la cadena de consulta e intenta acceder a los datos que no están autorizados para él / ella, entonces los estoy redirigiendo a la página de mensajes no autorizados, usando ActionFilterAttribute.
Sameer
3

Aquí hay una solución que también tiene en cuenta si está utilizando solicitudes Ajax.

using System;
using System.Web.Mvc;
using System.Web.Routing;

namespace YourNamespace{        
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class AuthorizeCustom : ActionFilterAttribute {
        public override void OnActionExecuting(ActionExecutingContext context) {
            if (YourAuthorizationCheckGoesHere) {               
                string area = "";// leave empty if not using area's
                string controller = "ControllerName";
                string action = "ActionName";
                var urlHelper = new UrlHelper(context.RequestContext);                  
                if (context.HttpContext.Request.IsAjaxRequest()){ // Check if Ajax
                    if(area == string.Empty)
                        context.HttpContext.Response.Write($"<script>window.location.reload('{urlHelper.Content(System.IO.Path.Combine(controller, action))}');</script>");
                    else
                        context.HttpContext.Response.Write($"<script>window.location.reload('{urlHelper.Content(System.IO.Path.Combine(area, controller, action))}');</script>");
                } else   // Non Ajax Request                      
                    context.Result = new RedirectToRouteResult(new RouteValueDictionary( new{ area, controller, action }));             
            }
            base.OnActionExecuting(context);
        }
    }
}
Miguel
fuente
1

Esto funciona para mí (asp.net core 2.1)

using JustRide.Web.Controllers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace MyProject.Web.Filters
{
    public class IsAuthenticatedAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (context.HttpContext.User.Identity.IsAuthenticated)
                context.Result = new RedirectToActionResult(nameof(AccountController.Index), "Account", null);
        }
    }
}



[AllowAnonymous, IsAuthenticated]
public IActionResult Index()
{
    return View();
}
mortenma71
fuente
0

podría heredar su controlador y luego usarlo dentro de su filtro de acción

dentro de su clase ActionFilterAttribute:

   if( filterContext.Controller is MyController )
      if(filterContext.HttpContext.Session["login"] == null)
           (filterContext.Controller as MyController).RedirectToAction("Login");

dentro de su controlador base:

public class MyController : Controller 
{
    public void  RedirectToAction(string actionName) { 
        base.RedirectToAction(actionName); 
    }
}

Contras. de esto es cambiar todos los controladores para heredar de la clase "MyController"

Muhammad Soliman
fuente