MVC Ajax Request Error handling

Download Sample Project

In previous post, how to use ajax in mvc application, I talked some basic concepts of using ajax in mvc application. The big assumption in that discussion was that everything works smoothly, there are not errors in execution and ajax returns response that we expected. In reality things do not always work as planned. We have to account for unexpected conditions, errors and other unhandled exceptions etc. In this post I will discuss following concepts related to MVC web applications.

  • Implementing custom AuthorizeFilter to extend authorization
  • Handling failed ajax request on client side to display error message

Authorize Ajax request using AuthorizeAttribute

AuthorizeAttribute does not have any direct relation to discussion on handling of errors in ajax requests. But I took the oppurunity to demonstrate how you can implement custom filters on your controllers. Authentication and authorization are the building blocks of secure application. It is important that you have authorize the requests coming in to your MVC application appropriately to prevent execution of unauthorized code.

If only requirement for your application is that caller needs to be authenticated, then you can use AuthorizeAttribute directly and there is no need for any custom implementation. But if there is more that needs to be done and can not be handled by AuthorizeAttribute class then you will need to implement new class that derives from AuthorizeAttribute. The following code snippet shows class that I implemented for demo project.

public class AsyncAuthorizeAttribute : AuthorizeAttribute
{
  protected override bool AuthorizeCore(HttpContextBase httpContext)
  {
     // TODO: Perform authorization check here. If the call is not
     //       authorized, then return false. Otherwise return true
     //       from this method.
    return base.AuthorizeCore(httpContext);
  }

  protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
  {
    if (filterContext.HttpContext.Request.IsAjaxRequest())
    {
      var urlHelper = new UrlHelper(filterContext.RequestContext);
      filterContext.Result = new ViewResult()
      {
        ViewName = "UnauthorizedAccess"
      };
    }
    else
    {
      base.HandleUnauthorizedRequest(filterContext);
    }
  }
}
    

There are two important methods that can you can override to control how authorization checks happen for method or class on which you put this attribute.

  • AuthorizeCore: This method is called to get a vote on if request is authorized or not. The base implementation will always return false if user has not logged in. This is the place where you can perform additional check. The method is passed the whole context of this request in HttpContextBase parameter. If you return false from this method, your request will send 401 status code back to caller.
  • HandleUnauthorizedRequest: If you are not looking into doing anything special if request is unauthorized then you do not have to override this method. You can see from my implementation, I am trying to handle unauthorized request for Ajax differently. This implementation is blocking send of 401 error code in response and instead sending a view that has some informaiton about the failure. Now you may be asking why not just let 401 status code goto client. I will discuss this in more detail now.

Handling Unauthorized Ajax Request

If you send 401 status code in response to Ajax request, you will end up a view something looking like below.

You can see that place where Ajax response was supposed to insert HTML for successful request, you have your login page displayed there. The reason is that when authorization failed, ASP.Net framework redirected the request to login page that was set in web.config file. This is not what you intended. You wanted to show message to the user that he or she needs to login if they want to execute the action on the page. This is why I have custom implementation in HandleUnauthorizedRequest method for Ajax request. I have created a partial view in project that contains the HTML that will be shown to user when authorization fails. Following snippet shows that partial view.

@{
    Layout = null;
}
<p>You will need to log in to this site to execute this operation! 
        Please click on the following link to proceed.</p>
<p>
    @Html.ActionLink("Login", "Login", "Account")
</p>
    

Now when this authorization fails, Ajax famework the following HTML into element specified by UpdateTargetId property value in AjaxOptions class when you set up the request.

UpdateTargetId and OnFailure

There is one little fine print that is missing in MVC documentation. HTML element indicated by UpdateTargetId id is only updated when Ajax request succeeds. When request fails, the framework will not insert any error message in that HTML element. So if you want to display some message to user or you want to know more about the error, then you will have to implement a client side function for OnFailure event handler. In that client side javascript code, you can extract error information from first parameter passed to the function. Following code snippet shows how I managed to display some details about the exception thrown from server in response to my Ajax request.

function onMessageSendFail(request, status) {
    var errorDiv = $('#errorresponse');
    errorDiv.html(request.responseText);
}
    

The detailed error message looked like below when i forced an exception from code.

Server Error in '/' Application.
--------------------------------------------------------------------------------
 Operation is not valid due to the current state of the object. 
Description: An unhandled exception occurred during the execution of the current web request. 
Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidOperationException: Operation is not valid due to 
the current state of the object.

Source Error: 
Line 22:         public ActionResult Index(Models.ContactUsViewModel contactUs)
Line 23:         {
Line 24:             throw new InvalidOperationException();
Line 25:             return View("ContactUsResponse", 200);
Line 26:         } 

Stack Trace: 
    

Sample project attached with this post contains all the code that demonstrates all the concepts that I discussed above.

comments powered by Disqus

Blog Tags