Перенаправление из атрибута фильтра действий

139

Каков наилучший способ сделать перенаправление в ActionFilterAttribute. Я ActionFilterAttributeвызвал IsAuthenticatedAttributeFilterи проверил значение переменной сеанса. Если переменная имеет значение false, я хочу, чтобы приложение перенаправляло на страницу входа. Я бы предпочел перенаправить, используя имя маршрута, SystemLoginоднако любой метод перенаправления на этом этапе будет в порядке.

ryanzec
источник

Ответы:

187

Установить filterContext.Result

С названием маршрута:

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

Вы также можете сделать что-то вроде:

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

Если вы хотите использовать RedirectToAction:

Вы можете создать открытый RedirectToActionметод на вашем контроллере ( предпочтительно на его базовом контроллере ), который просто вызывает защищенный RedirectToActionот System.Web.Mvc.Controller. Добавление этого метода позволяет для публичного вызова своих RedirectToAction контекстуальных из фильтра.

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

Тогда ваш фильтр будет выглядеть примерно так:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var controller = (SomeControllerBase) filterContext.Controller;
    filterContext.Result = controller.RedirectToAction("index", "home");
}
CRice
источник
8
Это работает, но разве не должен быть доступен метод RedirectToAction?
Бен Миллс
@BenMills, однако, есть, protectedтак что у вас не будет доступа к нему из фильтра.
Джеймс
10
Теперь у меня вопрос: почему Microsoft решила сделать этот фильтр, protectedдолжно быть какое-то разумное объяснение? Я чувствую себя очень грязно, переопределяя эту доступность, RedirectToActionне понимая, почему она была заключена в капсулу.
Мэтью Марлин
2
@MatthewMarlin - см. Ответ Сякура, чтобы найти правильный ответ для перенаправления на действие. Вы правы в том, что вам не следует вызывать контроллер напрямую из фильтра действий - это определение тесной связи.
NightOwl888
1
@Akbari вы пытались установить свойство Order атрибутов? Также FilterScope повлияет на порядок выполнения.
Крис
79

В качестве альтернативы перенаправлению, если он вызывает ваш собственный код, вы можете использовать это:

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

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

Это не просто перенаправление, но дает аналогичный результат без лишних затрат.

Сякур Рахман
источник
Вы помогли мне. Спасибо!
Эдгар Салазар
25
Обратите внимание, что вам не следует звонить actionContext.Result.ExecuteResultиз вашего фильтра действий - MVC сделает это автоматически после запуска фильтра действий (при условии, что actionContext.Resultон не равен нулю).
NightOwl888
12

Я использую MVC4, я использовал следующий подход, чтобы перенаправить пользовательский экран HTML при нарушении авторизации.

Расширить AuthorizeAttributeскажем CutomAuthorizer переопределить OnAuthorizationиHandleUnauthorizedRequest

Зарегистрируйтесь CustomAuthorizerв RegisterGlobalFilters.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{

    filters.Add(new CustomAuthorizer());
}

после идентификации unAuthorizedвызова доступа HandleUnauthorizedRequestи перенаправления на соответствующее действие контроллера, как показано ниже.


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
источник
9

Похоже, вы хотите повторно реализовать или, возможно, расширить AuthorizeAttribute. Если это так, вам следует убедиться, что вы наследуете это, а не так ActionFilterAttribute, чтобы ASP.NET MVC выполнял за вас большую часть работы.

Кроме того, вы хотите убедиться, что вы авторизуетесь, прежде чем выполнять какую-либо реальную работу в методе действия - в противном случае единственная разница между тем, кто вошел в систему и не будет, заключается в том, какую страницу вы видите, когда работа выполнена.

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

Есть хороший вопрос с ответом с более подробной информацией о SO.

Томас Ашан
источник
5

Попробуйте следующий фрагмент, это должно быть довольно ясно:

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);
  }
}
Мухаммед Солиман
источник
Это сработало для меня, я должен был проверить значения строки запроса, если какой-либо пользователь пытается изменить значения строки запроса и пытается получить доступ к данным, которые ему не разрешены, чем я перенаправляю их на страницу неавторизованных сообщений, используя ActionFilterAttribute.
Самер
3

Вот решение, которое также учитывает, если вы используете запросы 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);
        }
    }
}
Майк
источник
1

Это работает для меня (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
источник
0

Вы можете унаследовать свой контроллер, а затем использовать его внутри фильтра действий

внутри вашего класса ActionFilterAttribute:

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

внутри вашего базового контроллера:

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

Cons. это изменить все контроллеры для наследования от класса "MyController"

Мухаммед Солиман
источник