Adding to ViewData[] collection from AuthorizeAttribute Extension - asp.net-mvc

I wrote an extension class to customize my AuthorizeAttribute for my action methods and I'd like to be able to inject messages into my view when a certain condition is met. I"m using the below code to load up a shared view when a user is not authorized but it's not adding my message to my ViewData collection. Any ideas?
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (IsNotAuthorized)
{
filterContext.Result = new ViewResult { ViewName = "NotAuthorized" };
filterContext.Controller.ViewData["Message"] = "Go Away";
}
}
I've also tried setting my ViewData["Message"] collection item above the call to change the view with no success.

Have you tried;
filterContext.Result = new RedirectResult("Home/Index");
I don't know how to add the ViewData but this will get you to the not authorised controller at least.
I'll keep looking for code to add to view data in the mean time or until someone posts it.
edit
This may help;
Changing ActionExecutingContext values in Custom Filter Attribute

Related

Extending ASP.NET MVC Controller with redirection or 404 error in certain circumstances

I want to extend the Controller class to check whether a request matches certain criteria.
In particular, I want to check whether the user has been directed from a certain URL using the UrlReferrer proprety.
If the user has not come from a particular URL, it should return a 404 message, or in some cases redirect to a different URL altogether.
I was overriding OnActionExecuting to look like this:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!IsAuthenticRequest())
{
return new HttpNotFoundResult();
//or in some cases I want to redirect to a completely different URL.
}
}
But this does not work because its a void method.
How would I go about performing this task?
Thanks,
Mike
Even though the other answer is correct, I don't think it's a good solution for your scenario. I think what you need is a custom Action Filter like this:
public class MyActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!IsAuthenticRequest())
{
filterContext.Result = new HttpNotFoundResult();
}
}
}
Then, you can use this filter either at the Controller level (to apply it to all Actions in the Controller) or at the Action level:
[MyActionFilter]
public class HomeController : Controller
{
}
Yes, it's a void method, but inside the filterContext parameter you have a Result property. If the result is filled by the time the OnActionExecuting method finishes, the actual Action method is not even going to be called.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!IsAuthenticRequest())
{
filterContext.Result = new HttpNotFoundResult();
}
}
Alternatively, if you want this behavior to be consistent across different controllers, you might want to consider writing a filter instead. To understand more about it, please refer to: http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs
I've just finished implementing a feature such as this within our primary intranet application. My solution was to create a custom authorize filter as suggested by ataravati above,but to redirect according to certain circumstances.
public class UrCustomFilter:ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!IsAuthenticRequest())
{
if( cond1 == true)
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "controller", "Error" }, {"action", "404NotFound" } });
else{
//do another redirect here
}
}
}
}

ASP.NET MVC 4 - Portal site

I am new to MVC4 and please bear with me. I am building a portal where users will be shown different widgets. Lets just say widgets are some rectangular boxes with a title and a content. What would be a better way to implement this? i am planning to use partial views. And then call Html.renderaction on the aggregate view. Is this a good choice or is there a better way to do it?
2.)Also when the widget encounters any exception i would like to show a custom error message just on the widget area. I dont want the entire page to be redirected to an error page. Just the rectangle area alone.
#Html.RenderAction should do the work, for the exceptions a try/catch can help you:
[ChildActionOnly]
public ActionResult Widget(int id) {
try
{
var widget = Repository.GetWidget(id);
return PartialView(widget);
}
catch
{
return PartialView("WidgetErrorPage");
}
}
UPDATE:
In that case you can use an ActionFilter to handle the exceptions, like explained here Return View from ActionFilter or here Returning a view with it's model from an ActionFilterAttribute:
public class WidGetHandleException : ActionFilterAttribute
{
protected override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
filterContext.Result = new PartialViewResult
{
ViewName = "WidgetErrorPage",
ViewData = filterContext.Controller.ViewData,
TempData = filterContext.Controller.TempData,
};
}
}
And then decorate all your widget actions like this:
[ChildActionOnly]
[WidGetHandleException]
public ActionResult Widget()
{
}

How do I redirect user to another Controller Action from an ASP.MVC 3 action filter?

In building a custom ASP.MVC 3 Action Filter, how should I redirect the user to another action if my test fails? I would like to pass along the original Action so that I can redirect back to the original page after the user enters the missing preference.
In controller:
[FooRequired]
public ActionResult Index()
{
// do something that requires foo
}
in a custom filter class:
// 1. Do I need to inherit ActionFilterAttribute or implement IActionFilter?
public class FooRequired : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (TestForFoo() == false)
{
// 2. How do I get the current called action?
// 3. How do I redirect to a different action,
// and pass along current action so that I can
// redirect back here afterwards?
}
// 4. Do I need to call this? (I saw this part in an example)
base.OnActionExecuting(filterContext);
}
}
I'm looking for a simple ASP.MVC 3 filter example. So far my searches have lead to Ruby on Rails examples or ASP.MVC filter examples much more complicated than I need. I apologize if this has been asked before.
Here's a small code sample using one of my own Redirect filters:
public class PrelaunchModeAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//If we're not actively directing traffic to the site...
if (ConfigurationManager.AppSettings["PrelaunchMode"].Equals("true"))
{
var routeDictionary = new RouteValueDictionary {{"action", "Index"}, {"controller", "ComingSoon"}};
filterContext.Result = new RedirectToRouteResult(routeDictionary);
}
}
}
If you want to intercept the route, you can get that from the ActionExecutingContext.RouteData member.
With that RouteData member, you can get the original route:
var currentRoute = filterContext.RouteData.Route;
Etc... Does that help answer your question?
You can set the filterContext.Result to a RedirectToRouteResult:
filterContext.Result = new RedirectToRouteResult(...);

Changing the view in an ASP.NET MVC Filter

I want to redirect the user to a different view if they are using a mobile browser. I've decided I'd like to do this using MVC filters by applying it to actions which I want to have a mobile view.
I believe this redirect needs to happen in OnActionExecuted, however the filterContext does not contain information on the view - it does, however in OnResultExecuted, but by this time I believe it is too late to change the view.
How can I intercept the view name and change the ViewResult?
This is what I have in the result executed and what I'd like to have work in Action Executed.
public class MobilePageFilter : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if(filterContext.Result is ViewResult)
{
if (isMobileSite(filterContext.HttpContext.Session[SetMobile.SESSION_USE_MOBILE]))
{
ViewResult viewResult = (ViewResult)filterContext.Result;
string viewName = viewResult.ViewName;
filterContext.Result = new ViewResult
{
ViewName = "Mobile/" + viewName,
ViewData = viewResult.ViewData,
TempData = viewResult.TempData
};
}
}
base.OnResultExecuted(filterContext);
}
}
I would recommend you the following blog post which explains a better alternative to achieve what you are asking for rather than using action filters.
This is what I ended up doing, and wrapped up into a reusable attribute and the great thing is it retains the original URL while redirecting (or applying whatever result you wish) based on your requirements:
public class AuthoriseSiteAccessAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
// Perform your condition, or straight result assignment here.
// For me I had to test the existance of a cookie.
if (yourConditionHere)
filterContext.Result = new SiteAccessDeniedResult();
}
}
public class SiteAccessDeniedResult : ViewResult
{
public SiteAccessDeniedResult()
{
ViewName = "~/Views/SiteAccess/Login.cshtml";
}
}
Then just add the attribute [SiteAccessAuthorise] to your controllers you wish to apply the authorisation access to (in my case) or add it to a BaseController. Make sure though the action you are redirecting to's underlying controller does not have the attribute though, or you'll be caught in an endless loop!

Setting result for IAuthorizationFilter

I am looking to set the result action from a failed IAuthorizationFilter. However I am unsure how to create an ActionResult from inside the Filter. The controller doesn't seem to be accible from inside the filter so my usual View("SomeView") isn't working. Is there a way to get the controler or else another way of creating an actionresult as it doesn't appear to be instantiable?
Doesn't work:
[AttributeUsage(AttributeTargets.Method)]
public sealed class RequiresAuthenticationAttribute : ActionFilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext context)
{
if (!context.HttpContext.User.Identity.IsAuthenticated)
{
context.Result = View("User/Login");
}
}
}
You should look at the implementation of IAuthorizationFilter that comes with the MVC framework, AuthorizeAttribute. If you are using forms authentication, there's no need for you to set the result to User/Login. You can raise a 401 HTTP status response and ASP.NET Will redirect to the login page for you.
The one issue with setting the result to user/login is that the user's address bar is not updated, so they will be on the login page, but the URL won't match. For some people, this is not an issue. But some people want their site's URL to correspond to what the user sees in their browser.
You can instantiate the appropriate ActionResult directly, then set it on the context. For example:
public void OnAuthorization(AuthorizationContext context)
{
if (!context.HttpContext.User.Identity.IsAuthenticated)
{
context.Result = new ViewResult { ViewName = "Whatever" };
}
}

Resources