Search code examples
asp.net-mvcjsonexceptioncustom-action-filter

Handling Ajax cal exceptions via Custom Action Filters


I am implementing an authorization mechanizm for my MVC application via Custom Action Filters.

I have provided the following Custom Action Filter for authorization:

[AttributeUsageAttribute(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class AuthorizationFilterAttribute : ActionFilterAttribute
{
    public AuthorizationEntity Entity { get; set; }
    public AuthorizationPermission Permission { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        AuthorizationResult result = null;
        // Base actions (Authentication first)
        base.OnActionExecuting(filterContext);
        BaseController controller = filterContext.Controller as BaseController;
        if (controller != null)
        {   // Base actions (Authorizatioın next)
            User usr = controller.currentUser;
            AuthorizationResult ar = AuthorizationManager.GetAuthorizationResult(this.Entity, this.Permission, usr.UserId, usr.RoleId);
            if (!ar.IsAuthorized)
            {
                throw new UnauthorizedAccessException(ar.Description);
            }
            // Authorized, continue
            return;
        }
    }
}

And in my Base Controller class I am handling UnauthorizedAccessException type Exceptions and redirect them to a warning page via the following code

protected override void OnException(ExceptionContext filterContext)
{
    if (filterContext.Exception is UnauthorizedAccessException)
    {
        if (!filterContext.HttpContext.Request.IsAjaxRequest())
        {
            Exception ex = filterContext.Exception;

            filterContext.ExceptionHandled = true;
            filterContext.Result = new ViewResult()
            {
                ViewName = "UnauthorizedAccess"
            };
        }
        else
        {
            throw filterContext.Exception;
        }
    }
}

This mechanism is OK for actions which return ActionResult. But I also have some AJAX calls, which I don't want to redirect to a warning page but would ilke to display a warning pop-up instead. Thi is why I have checked if the request is an Ajax call is not.

I am using the following code to make Ajax calls:

$.ajax({
    type: "POST",
    url: "AjaxPostMethodName",
    dataType: "json",
    data:
        {
            postval: [some value here]
        },
    success: function (msg) {
        // Do some good actions here
    },
    error: function (x, t, m, b) {
        // Display error
        alert(m);
    }
})

which goes to the following method on the Controller

public JsonResult AjaxPostMethodName(string postval)
{
    try
    {
       // Some cool stuff here
        return Json(null);
    }
    catch (Exception ex)
    {
        Response.StatusCode = UNAUTHORIZED_ACCESS_HTTP_STATUS_CODE;
        return Json(ex.Message);
    }
}

But when I fail the authorization check it directly shows the "Internal Server Error" message instead of falling to the catch block of AjaxPostMethodName method and displaying the proper message.

How can I make such code display filterContext.Exception instead of static "Internal Server Error" message?

Regards.


Solution

  • I finally found the answer to my solution in another Stack Overflow post (Can I return custom error from JsonResult to jQuery ajax error method?). I should use JsonExceptionFilterAttribute as follows:

    public class JsonExceptionFilterAttribute : FilterAttribute, IExceptionFilter
    {
        public void OnException(ExceptionContext filterContext)
        {
            if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.HttpContext.Response.StatusCode = 500;
                filterContext.ExceptionHandled = true;
    
                string msg = filterContext.Exception.Message;
                if (filterContext.Exception.GetType() == Type.GetType("System.UnauthorizedAccessException"))
                {
                    msg = "Unauthorized access";
                }
    
                filterContext.Result = new JsonResult
                {
                    Data = new
                    {
                        errorMessage = msg
                    },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
            }
        }
    }