1

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.

user3021830
  • 2,784
  • 2
  • 22
  • 43

3 Answers3

1

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
            };
        }
    }
}
Community
  • 1
  • 1
user3021830
  • 2,784
  • 2
  • 22
  • 43
  • I was using return new HttpStatusCodeResult, it was not returning error and showing 504 receive failed. Now I used return new JsonResult, and the problem solved. :) – Bimal Das Oct 26 '18 at 08:43
0

Your OnException method will get called when there is Unhandled exception in your code. And in your ajax method AjaxPostMethodName you have put your code in try catch blcok. So any exception in this method will not go to your OnException method.

Mairaj Ahmad
  • 14,434
  • 2
  • 26
  • 40
  • Unfortunately that does not work. OnException runs before AjaxPostMethodName name and after throwing an exception actual method does not run. – user3021830 Jan 08 '15 at 17:55
0

I've just checked the Response.StatusCode behavior and for me it works.

Index.cshtml

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>


<script type="text/javascript">
    $(document).ready(function () {
        alert('doc ready');
        $.ajax({
            type: "POST",
            url: '@Url.Action("AjaxPostMethodName")',                
            dataType: "json",
            data:
                {
                    test: '10'
                },
            success: function (msg) {
                // Do some good actions here
                alert('success');
                alert(msg);
            },
            error: function (x, t, m, b) {
                // Display error
                alert('error');
            }
        });
    });
</script>

HomeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public JsonResult AjaxPostMethodName(string postval)
        {
            Response.StatusCode = 401;
            return Json("test");
        }

    }
}

When I set Response.StatusCode to 200 it calls success, when 401 it calls error.

Please verify whether other parts of your code don't interfere with it somehow.

You could try also following workaround - if AjaxPostMethodName throws exception returned JSON has a flag isValid and a message errorMessage, so in your ajax success method you can just check whether isValid is okay and handle error.

Arkadiusz Kałkus
  • 17,101
  • 19
  • 69
  • 108