Ensure that a specific exception always results in a given HTTP response status code


Ensure that a specific exception always results in a given HTTP response status code



Requirement



I have an ASP.Net MVC application that works with a number of different libraries. As with most libraries, various function calls may cause one of many different exceptions to be thrown.



Currently, whenever any exception is thrown, then MVC application 'handles' them and returns an "internal server error" (code 500) back to the client (eg. web browser).



This is fine for most cases, however, there is one specific exception type (in this case UnauthorizedAccessException) that I would like to result in an "Unauthorized" (code 401) status being sent in the response, instead of the usual 500 error.


UnauthorizedAccessException



Current Attempt



I did a fair bit of research and it looks like the bets way to 'catch' all exceptions and process them is by using the Application_Error method. So I tried the following implementation of Application_Error in the MvcApplication class:


Application_Error


Application_Error


MvcApplication


protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();

if(ex is UnauthorizedAccessException)
{
Response.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
}
}



Problem



The problem here is that although I can debug and see that Response.StatusCode is being set to 401, the client/browser is still receiving a 500 error.


Response.StatusCode



I expect that there is a very good reason why this is happening, but I have exhausted my brain thinking of search terms that will get me the answer I need.



Question



In short, what do I need to do in order to get the behaviour I am looking for?



For additional information, ultimately what I want is for an UnauthorizedAccessException to have the same behaviour as how MVC handles unauthenticated requests (which redirects to login page). However, I also need it to work for AJAX requests in that my javascript can check for 401 error and do some specific logic (in which case a response redirect to login page is not workable)


UnauthorizedAccessException


401




1 Answer
1



A clever way to go about doing that is to create a Base Controller that your controllers inherit over the default Controller. There you inherit the default Controller class and override the OnException method.


public abstract class BaseController : Controller
{
protected override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
var responseCode = Response.StatusCode;
var exception = filterContext.Exception;
switch (exception.GetType().ToString())
{
case "UnauthorizedAccessException":
responseCode = 401;
filterContext.ExceptionHandled = true;
break;
}

Response.StatusCode = responseCode;

base.OnException(filterContext);
}
}



The trick that makes it work is filterContext.ExceptionHandled = true; if you don't set this to true, the server will return 500.


filterContext.ExceptionHandled = true;



Your controllers will inherit the BaseController;


BaseController


public class UserController : BaseController
{
public ActionResult Index(){
return View();
}
}



What you'll need to add to this code is your redirect to the login page, to your OnException method (if needed). I would do it for you but I don't have enough time to write and test it for you.. currently waiting for automated tests to finish.. :-)


OnException



Edit:



I did not realize your view could throw errors too, that obviously won't be handled by the controller.



In this case we can revert to the original Application_Error method on Global.asax.



What we need is two lines of code..


Response.StatusCode = 401;
Response.End();



First line sets the status code to 401,
Second line ends the execution at this point and triggers EndRequest event, so the StatusCode won't be modified to 500.



If you want to attach a message with your response:


Response.Write("Oops, you're not authorized...");



It would be a good idea to call Response.Clear(); before starting to modify the response object inside your Error Handler.


Response.Clear();





Conveniently, I already had a base controller that I was using. Made some changes to handle ajax requests differently, done some testing, and everything appears to be working as desired. Thanks for your time and answer!
– musefan
Jun 29 at 8:43





Actually, this doesn't catch all exceptions. If an exception is throw from the view page, then it bypasses this method and only goes to Application_Error. I am currently looking for a solution, but if you can think of anything of the top of your head it would be appreciated? It specifically coming from the _Layout view
– musefan
Jun 29 at 10:12



Application_Error





@musefan Of course, I didn't realize you'll be throwing errors inside views.. Try my edit.
– Adriani6
Jun 29 at 10:32





Sussed it. Because I can be sure AJAX requests will be caught in OnException (as you can't AJAX from a view render), then all I need in Application_Error is to check exception type and then do HttpContext.Current.Response.Redirect("some url"); to handle non-ajax error in view. All my test cases seem to pass now :)
– musefan
Jun 29 at 11:55



OnException


Application_Error


HttpContext.Current.Response.Redirect("some url");





Might be a chance for some extra rep here if you happen to know the solution!
– musefan
Jun 29 at 14:15






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Comments

Popular posts from this blog

paramiko-expect timeout is happening after executing the command

how to run turtle graphics in Colaboratory

Export result set on Dbeaver to CSV