I’ve seen a great answer to a similar question which explains, by inheriting all controllers from a new base class decorated with your own ActionFilter attribute, how you could apply some logic to all requests to your site.
I’d like to find a way to do that based on the area of a site my user is visiting.
For example, I will have a Product controller with a View action but I want to allow that to be used for the two following urls:
/Product/View/321 – display product id 321 to ‘normal’ users
/Admin/Product/View/321 – use the same View controller but spit out extra functionality for my admin users.
I could pass “admin” in as a parameter named “user” into my view action on my product controller to show extra information for administrators, a method for doing that is shown here. But what I’d then need to do is confirm my user was allowed to view that url. I don’t want to decorate my Product controller with an ActionAttribute that checks for authentication because when unauthenticated users (and logged in administrators) view it at /Product/View/321, I want them all to see the standard view.
So what I’d like to do, is described below in pseudo-code:
When a url in the format “{userlevel}/{controller}/{action}/{id}” is called, I’d like like to call another controller that does the authentication check and then ‘chain’ to the original {controller} and pass through the {action}, {id} and {userlevel} properties.
How would I do that?
(I know that the over-head for doing a check on every call to the controller is probably minimal. I want to do it this way because I might later need to do some more expensive things in addition to user authentication checks and I’d prefer to only ever run that code for the low-traffic admin areas of my site. There seems no point to do these for every public user of the site)
At first I thought this might be as simple as adding a new route like this:
and then have a controller something like this:
However, unfortunately all the options you have available in order to do the redirect (i.e. Redirect, RedirectToAction & RedirectToRoute) all do a 302 style redirect. Basically this means that your
/Admin/Product/Whateverwill execute & then bounce back to the browser telling it to redirect to/Product/Whateverin a totally new request, which means you’ve lost your context. I don’t know of a clean way of keeping the redirect server side (i.e. like aServer.Transferof old), apparently neither does the SO community…(obviously, this is a non-solution, since it doesn’t solve your problem, but I thought I’d put it here anyway, in case you could use the ideas in some other way)
So, what’s an actual solution to the problem then? Another idea is to use an ActionFilter (yes I know you said you didn’t want to do so, but I think the following will serve your purposes). Add a new route like this:
and then add an ActionFilter like this (that you could apply to all requests via a base controller object as you mentioned):
So although it is using an ActionFilter that will apply to all requests, the only extra work done in most normal cases (i.e. a request for
/Product/Whatever), is a single check of that bit of route data (userLevel). In other words, you should really see a performance hit for normal users since you’re only doing the full auth check and extra admin work if they requested via/Admin/Product/Whatever.