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()
{
}
Related
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
}
}
}
}
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!
I use this pattern all over the place to grab data from the database and display a view:
public ActionResult Index(int? id)
{
RequestViewModel model;
model = this.ClientRepository.GetRequest(id);
return View("~/Views/Requests/Index.aspx", model);
}
If the repository returns null, which is the case if the record does not exist, then my page craps out and throws an error because the model is null.
I’d like to show a friendly “the requested record cannot be found” message instead of the yellow page of death or a generic “an error occurred” page.
What’s the recommended pattern to handle “normal” errors as opposed to unhandled exceptions?
Thanks,
Rick
You could write an action filter:
public class NullModelCheckerAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var viewResult = filterContext.Result as ViewResultBase;
if (viewResult != null && viewResult.ViewData.Model == null)
{
// If the action selected a view to render and passed a null model
// render the NotFound.aspx view
var result = new ViewResult();
result.ViewName = "~/Views/Errors/NotFound.aspx";
filterContext.HttpContext.Response.StatusCode = 404;
filterContext.Result = result;
}
}
}
And then decorate your base controller (that all your controllers derive from) with this attribute:
[NullModelChecker]
public class BaseController: Controller
{ }
This way your current code stays untouched.
--
UPDATE:
In ASP.NET MVC 3 you could register your action filter globally without even decorating your base controller with it. Simply add the following to your Application_Start in Global.asax:
GlobalFilters.Filters.Add(new NullModelCheckerAttribute());
I'm not familiar with ASP.NET MVC. I'm familiar though with Spring MVC.
Why can't you just put a simple if-else condition? Like this one:
public ActionResult Index(int? id)
{
RequestViewModel model;
model = this.ClientRepository.GetRequest(id);
if (model == null) {
return View("~/Views/Requests/FriendlyError.aspx");
}
return View("~/Views/Requests/Index.aspx", model);
}
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
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" };
}
}