Vom Aktionsfilterattribut umleiten

139

Was ist der beste Weg, um eine Weiterleitung in einem ActionFilterAttribute. Ich habe einen ActionFilterAttributeAufruf IsAuthenticatedAttributeFilterund das hat den Wert einer Sitzungsvariablen überprüft. Wenn die Variable false ist, soll die Anwendung zur Anmeldeseite umleiten. Ich würde es vorziehen, mit dem Routennamen umzuleiten, SystemLoginaber jede Umleitungsmethode an dieser Stelle wäre in Ordnung.

Ryanzec
quelle

Antworten:

186

Setzen Sie filterContext.Result

Mit dem Routennamen:

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

Sie können auch Folgendes tun:

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

Wenn Sie verwenden möchten RedirectToAction:

Sie können RedirectToActionauf Ihrem Controller ( vorzugsweise auf seinem Basis-Controller ) eine öffentliche Methode erstellen , die einfach den geschützten RedirectToActionvon aufruft System.Web.Mvc.Controller. Durch Hinzufügen dieser Methode können Sie RedirectToAction vom Filter aus einen öffentlichen Aufruf an Sie senden.

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

Dann würde Ihr Filter ungefähr so ​​aussehen:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var controller = (SomeControllerBase) filterContext.Controller;
    filterContext.Result = controller.RedirectToAction("index", "home");
}
CRice
quelle
8
Dies funktioniert, sollte aber keine RedirectToAction-Methode verfügbar sein?
Ben Mills
@BenMills gibt es jedoch, so protecteddass Sie nicht über den Filter darauf zugreifen können.
James
10
Meine Frage ist nun, warum Microsoft beschlossen hat, diesen Filter protectedzu erstellen. Es muss eine vernünftige Erklärung geben. Ich fühle mich sehr schmutzig, diese Zugänglichkeit neu zu definieren, RedirectToActionohne zu verstehen, warum sie überhaupt gekapselt wurde.
Matthew Marlin
2
@MatthewMarlin - In der Antwort von Syakur finden Sie die richtige Antwort für die Umleitung zu einer Aktion. Sie haben Recht, dass Sie einen Controller nicht direkt von einem Aktionsfilter aus aufrufen sollten - das ist die Definition einer engen Kopplung.
NightOwl888
1
@Akbari Haben Sie versucht, die Order-Eigenschaft der Attribute festzulegen? Auch FilterScope wirkt sich auf die Ausführungsreihenfolge aus.
CRice
79

Alternativ zu einer Umleitung können Sie Folgendes verwenden, wenn Sie Ihren eigenen Code aufrufen:

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

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

Es handelt sich nicht um eine reine Weiterleitung, sondern um ein ähnliches Ergebnis ohne unnötigen Overhead.

Syakur Rahman
quelle
Du hast mir geholfen. Vielen Dank!
Edgar Salazar
25
Beachten Sie, dass Sie nicht actionContext.Result.ExecuteResultinnerhalb Ihres Aktionsfilters aufrufen sollten - MVC führt dies automatisch aus, nachdem der Aktionsfilter ausgeführt wurde (vorausgesetzt, er actionContext.Resultist nicht null).
NightOwl888
12

Ich verwende MVC4 und habe den folgenden Ansatz verwendet, um einen benutzerdefinierten HTML-Bildschirm bei einem Verstoß gegen die Autorisierung umzuleiten.

Erweitern Sie AuthorizeAttributesay CutomAuthorizer überschreiben Sie das OnAuthorizationundHandleUnauthorizedRequest

Registrieren Sie die CustomAuthorizerin der RegisterGlobalFilters.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{

    filters.Add(new CustomAuthorizer());
}

nach Identifizierung des unAuthorizedZugriffsaufrufs HandleUnauthorizedRequestund Weiterleitung an die betreffende Controller-Aktion wie unten gezeigt.


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" }

                                         });

    }
}
user2834076
quelle
9

Es hört sich so an, als ob Sie neu implementieren oder möglicherweise erweitern möchten AuthorizeAttribute. In diesem Fall sollten Sie sicherstellen, dass Sie dies erben und nicht ActionFilterAttribute, damit ASP.NET MVC mehr für Sie erledigt.

Außerdem möchten Sie sicherstellen, dass Sie eine Autorisierung durchführen, bevor Sie die eigentliche Arbeit in der Aktionsmethode ausführen. Andernfalls besteht der einzige Unterschied zwischen angemeldet und nicht darin, welche Seite Sie nach Abschluss der Arbeit sehen.

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);
    }
}

Es gibt eine gute Frage mit einer Antwort mit mehr Details hier auf SO.

Tomas Aschan
quelle
5

Versuchen Sie das folgende Snippet, es sollte ziemlich klar sein:

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
quelle
Dies funktionierte für mich. Ich musste die Werte der Abfragezeichenfolgen überprüfen, wenn ein Benutzer versucht, die Werte der Abfragezeichenfolgen zu ändern und auf Daten zuzugreifen, die für ihn nicht autorisiert sind, als ich sie mithilfe von ActionFilterAttribute auf eine nicht autorisierte Nachrichtenseite umleite.
Sameer
3

Hier ist eine Lösung, die auch berücksichtigt, wenn Sie Ajax-Anforderungen verwenden.

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);
        }
    }
}
Mike
quelle
1

Das funktioniert bei mir (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
quelle
0

Sie könnten Ihren Controller erben und ihn dann in Ihrem Aktionsfilter verwenden

in Ihrer ActionFilterAttribute-Klasse:

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

in Ihrem Basis-Controller:

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

Nachteile Dazu müssen alle Controller so geändert werden, dass sie von der Klasse "MyController" erben

Muhammad Soliman
quelle