Archive for March 2010
Two ways to handle unauthorized requests to Ajax actions in ASP.NET MVC 2
Problem: I have created a view that posts to an action via Ajax with the expectation that the action will return the requested data or an empty string. Even better, I would like it to be configurable to return whatever value I see fit.
The problem arises when I decorate the called action with the [Authorize] attribute. If the request is not authorized and I have a loginUrl configured in my web.config, my ajax request will return the html output of my loginUrl view. That is undesirable.
Solution #1: I need to implement a custom ActionFilterAttribute that I can use on the ajax action to handle the request appropriately. Here is the code for my ActionFilterAttribute:
public class AjaxAuthorizeAttribute : ActionFilterAttribute { public string View { get; set; } private bool renderView; public override void OnActionExecuting(ActionExecutingContext filterContext) { if (!filterContext.HttpContext.Request.IsAuthenticated && filterContext.HttpContext.Request.IsAjaxRequest()) { renderView = true; } base.OnActionExecuting(filterContext); } public override void OnResultExecuting(ResultExecutingContext filterContext) { if (renderView) { filterContext.Result = new ViewResult { ViewName = View }; filterContext.Result.ExecuteResult(filterContext.Controller.ControllerContext); return; } base.OnResultExecuting(filterContext); } }
And, here is how I would decorate my ajax action in my controller class:
[AjaxAuthorize(View="AjaxAuthorizeError")] public ActionResult AjaxRequest() { return View(); }
That would handle the issue by checking whether the request is authenticated. If it isn’t authenticated and the request is being submitted via ajax, a specified view will get called. The content of that view determines what my ajax call will receive back when the request is not authenticated.
Note: There is no default view page being rendered if one is not passed to the ActionFilterAttribute. That’s room for improvement.
Solution #2: I can extend the existing Authorize attribute by inheriting from the AuthorizeAttribute class. Here is the code that extends the Authorize attribute:
public class AjaxAuthorizeOverrideAttribute : AuthorizeAttribute { public string View { get; set; } protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (!filterContext.HttpContext.Request.IsAjaxRequest()) { base.HandleUnauthorizedRequest(filterContext); return; } filterContext.Result = new ViewResult { ViewName = View }; filterContext.Result.ExecuteResult(filterContext.Controller.ControllerContext); } }
Here is the decorator for the ajax action in the controller class:
[AjaxAuthorizeOverride(View="AjaxAuthorizeError")] public ActionResult AjaxRequest() { return View(); }
Note: Again, there is no default view page being rendered.