204

I have tried two ways: Response.Redirect() which does nothing, as well as calling a new method inside of the Base Controller that returns an ActionResult and have it return RedirectToAction()... neither of these work.

How can I do a redirect from the OnActionExecuting method?

MetaGuru
  • 42,847
  • 67
  • 188
  • 294

4 Answers4

384
 public override void OnActionExecuting(ActionExecutingContext filterContext)
 {
    ...
    if (needToRedirect)
    {
       ...
       filterContext.Result = new RedirectResult(url);
       return;
    }
    ...
 }
womp
  • 115,835
  • 26
  • 236
  • 269
  • 79
    Instead of `new RedirectResult(url)` you could also use `new RedirectToAction(string action, string controller)`. This may have been added to MVC after you posted your answer. Your solution put me on the right track anyway. – Manfred Apr 22 '12 at 02:00
  • Wont this execute what is in your current action? Wouldn't this be a security flaw? Let's say there is a action that deletes a user routed by /Admin/Delete/4. If your condition is to check weither you are an admin, and redirect if not. The user 4 will be deleted, even if you end up redirected, correct? – Pluc Mar 19 '13 at 17:41
  • 3
    @Pluc - OnActionExecuting happens first. If you set the context.Result, then the redirect happens before the action executes. (Verified by personal testing/debugging.) – James Apr 22 '13 at 19:56
  • @James I never said it did. What I wasn't sure is if the action would still execute if the ActionExecutingContext result wasn't changed to a RedirectResult since you never actually tell him to avoid running it. You only fill the context's result. But I've tested and confirmed that setting this result WILL bypass running the action. So my previous comment was indeed wrong. – Pluc Apr 22 '13 at 20:16
  • 41
    @Manfred Note, the assignment should be done to the method (without `new`) RedirectToAction: `filterContext.Result = RedirectToAction(string action, string controller);` – cederlof Jun 03 '13 at 10:35
  • 1
    @Manfred I just wanted to add that you wouldn't new up the RedirectToAction. It's just filterContext.Result = RedirectToAction(..) – Sinaesthetic Mar 28 '14 at 15:24
  • Thanks @cederlof for your comment. I agree. 'new' is not required. – Manfred Mar 31 '14 at 01:38
  • 6
    The name 'RedirectToAction' does not exist in the current context?? – Dan Hastings Apr 29 '16 at 16:27
  • 1
    @Pluc I tried this recently on MVC 5 and the action was being executed, even with the `filterContext.Result` set to redirect to some action. I blocked the action from being executed by following the action with a call to `Dispose()`. *Both* redirection occurred *and* the action was prevented. e.g `filterContext.Result = RedirectToAction("Index", "Home"); Dispose(); //Important` – Reuel Ribeiro Jun 20 '16 at 12:56
  • @ReuelRamosRibeiro I would not suggest messing around with ASP.NET's life cycle by calling dispose. It may be work but definitely a code smell. – Pluc Jun 20 '16 at 18:03
  • @Manfred won't `RedirectToAction` just re-call `OnActionExecuting` again? In other words, one redirect will cause two calls to `OnActionExecuting` which seems bad to me. – Kellen Stuart Feb 14 '20 at 22:25
  • @KolobCanyon That may be true but I think that it's not necessarily a code smell. I can see scenarios where you'd consider encapuslating the call to `RedirectToAction()` in a different method which in turn is used in multiple places. This may help avoid code duplication in some scenarios. I've see this at several of our clients. – Manfred Mar 02 '20 at 19:33
  • 1
    @cederlof Yes, you are quite right. `new` shouldn't be used. I don't intend to remove my comment, though, as it would destroy the flow of the conversation in the comments. I'm hoping this is the right thing to do. – Manfred Mar 02 '20 at 19:35
62

It can be done this way as well:

filterContext.Result = new RedirectToRouteResult(
    new RouteValueDictionary
    {
        {"controller", "Home"},
        {"action", "Index"}
    }
);
Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
Randy Burden
  • 2,611
  • 1
  • 26
  • 34
41

Create a separate class,

    public class RedirectingAction : ActionFilterAttribute
    {
      public override void OnActionExecuting(ActionExecutingContext context)
      {
        base.OnActionExecuting(context);

        if (CheckUrCondition)
        {
            context.Result = new RedirectToRouteResult(new RouteValueDictionary(new
            {
                controller = "Home",
                action = "Index"
            }));
        }
      }
   }

Then, When you create a controller, call this annotation as

[RedirectingAction]
public class TestController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}
K.Kirivarnan
  • 828
  • 9
  • 15
5

If the redirected controller inherit from the same baseController where we override the OnActionExecuting method cause recursive loop. Suppose we redirect it to login action of account controller, then the login action will call OnActionExecuting method and redirected to the same login action again and again

... So we should apply a check in OnActionExecuting method to check weather the request is from the same controller if so then do not redirect it login action again. here is the code:

protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
   try
   {
      // some condition ...
   }
   catch
   {
      if (filterContext.Controller.GetType() != typeof(AccountController))
      {
         filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary {
              { "controller", "Account" }, 
              { "action", "Login" } 
         });
      }
   }
}
dcansyn
  • 78
  • 6
abdul hameed
  • 331
  • 4
  • 6
  • 1
    try { int CompanyId = UserContext.Company.CompanyId; } catch { if (filterContext.Controller.GetType() != typeof(AccountController)) { filterContext.Result = new RedirectToRouteResult( new RouteValueDictionary { { "controller", "Account" }, { "action", "Login" } }); } } – abdul hameed Jul 01 '16 at 06:38