RedirectToAction in Filter Attributes
Thankfully, ASP.NET MVC now has filters, seemingly called thus because that's what they are called in Monorail whereas "interceptors" would probably be a more appropriate name.
So I did what I imagine ninety percent of people are going to use the thing for: created a filter to authenticate a controller/action. You know the one. If you aren't authenticated then redirect to the login page. It's a problem that apparently still hasn't been solved to everyone's satisfaction given the number of solutions out there.
I turn to my natural resource, CodeCampServer, and check out their solution (off-topic code has been removed):
public override void OnActionExecuting(FilterExecutingContext filterContext) { if(!filterContext.HttpContext.User.Identity.IsAuthenticated) { filterContext.Cancel = true; //no way to access RedirectToAction() or Url.Action() here.... filterContext.HttpContext.Response.Redirect("~/login?redirectUrl=" + HttpUtility.UrlEncode(filterContext.HttpContext.Request.Url.PathAndQuery)); } }
Alas, my crest has fallen. No way to access RedirectToAction or Url.Action from within a filter attribute. They are protected or internal, I don't care which because neither will help me.
I'm surprised more people haven't mentioned one of the major reasons I see for not redirecting to hard-coded URLs. I'm developing on Windows XP and *not* using Cassini. Which means I've had to use .mvc in my routes as per the handy little comment in the default Global.asax.cs. But when I deploy to an IIS 7.0-ish environment, it's nice to know I can change the route table in one place and not have to worry about hard-coded URLs anywhere.
So I'm not liking that little hard-coding in there. But here's where it's nice to have the actual source code for MVC (and on another side note: I'd reckon the CodePlex project should be named in such a way that it actually shows up when you search for "MVC" on that site, as opposed to the generic name it has now). What I want is what happens when I do a RedirectToAction.
Taking a look through the RedirectToAction code, I came up with something I'm not going to show you. It worked like a charm but after I wrote it, I found an even better method from SteveSanderson which is a slight variation on the theme:
public override void OnActionExecuting(FilterExecutingContext filterContext) { if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { filterContext.Cancel = true; var requestContext = new RequestContext( filterContext.HttpContext, filterContext.RouteData ); var values = new RouteValueDictionary(new { Action = "Index", Controller = "Login" }); var vpd = RouteTable.Routes.GetVirtualPath(requestContext, values); var target = vpd.VirtualPath; var url = HttpUtility.UrlEncode(filterContext.HttpContext.Request.Url.PathAndQuery); filterContext.HttpContext.Response.Redirect(target + "?redirectUrl=" + url); } }
Please note the relative nature of the term "even better". This is by no means ideal but my NOR (Naive Optimism Radar) tells me that this will get better.
Final side note: Yes, there's nothing stopping me from using FormsAuthentication.LoginUrl instead of a controller action but c'mon, it's so 1.1.
Kyle the Nouveau