How to handle exception in ASP.Net MVC?
We can use different options provided by the dot.net framework to handle exception in ASP.Net MVC.
· Use traditional try… catch … finally block
· Use HandleError attribute to handle errors.
· Override OnException method to handle errors.
· Use HandleErrorAttribute to handle errors.
· Use webconfig file to handle HTTP error codes. Below are the list of HTTP Error Code which we can make an entry in the web config file and redirect to a user friendly error page.
Client Errors ( 400 Series )
|
Server Errors ( 500 Series )
|
400 Bad Request
401 Unauthorized
402 Payment Required
403 Forbidden
404 Not Found
405 Method Not Allowed
406 Not Acceptable
407 Proxy Authentication Required
408 Request Timeout
409 Conflict
410 Gone
411 Length Required
412 Precondition Failed
413 Request Entity Too Large
414 Request-URI Too Long
415 Unsupported Media Type
416 Requested Range Not Satisfiable
417 Expectation Failed
418 I'm a teapot (RFC 2324)
420 Enhance Your Calm (Twitter)
422 Un processable Entity (WebDAV)
423 Locked (WebDAV)
424 Failed Dependency (WebDAV)
425 Reserved for WebDAV
426 Upgrade Required
428 Precondition Required
429 Too Many Requests
431 Request Header Fields Too Large
444 No Response (Nginx)
449 Retry With (Microsoft)
450 Blocked by Windows Parental Controls (Microsoft)
451 Unavailable For Legal Reasons
499 Client Closed Request (Nginx)
|
500 Internal Server Error
501 Not Implemented
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout
505 HTTP Version Not Supported
506 Variant Also Negotiates (Experimental)
507 Insufficient Storage (WebDAV)
508 Loop Detected (WebDAV)
509 Bandwidth Limit Exceeded (Apache)
510 Not Extended
511 Network Authentication Required
598 Network read timeout error
599 Network connect timeout error
|
· Use Global Error Handling in ASP.Net MVC. This is the best practice to handle the errors. If error handling is not done or not caught at Controller level we can handle the error at global level.
Below is the example where we have implement all above 6 processes to explain the error handling in MVC.
Create a new MVC project named : ExceptionHandling
You will find list of folder and other files those are created automatically. Go to the HomeController and through an exception from Index Action Method as follows:
using System;
using System.Web.Mvc;
namespace ExceptionHandling.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
throw new Exception();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
Now build and run the application. You will get below screen when navigate to http://localhost:4989/home/index (Port number may differ for your system)
As we are troughing error we will get the above yellow screen. We can handle this error in more better way and navigate to a user friendly page instead of showing this yellow screen, which contains more specific to developer code which opens door for hackers.
1. Handling HTTP Code Errors and Normal Errors
Go to the root Web.config file of the project add the below two lines of code.
<system.web>
<customErrors mode="On">
</customErrors>
<compilation debug="true" targetFramework="4.7.2"/>
<httpRuntime targetFramework="4.7.2"/>
</system.web>
Now look at the output.
Now let discuss how you are getting this error after adding
<customErrors mode="On">
</customErrors>
Go to the shared folder where you will get a Error.cshtml page as follows.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Error</title>
</head>
<body>
<hgroup>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
</hgroup>
</body>
</html>
When any error occurs it is automatically redirected to this page. So internally how it works. If you will look into the Global.asax file you find a section RegisterGlobalFilters.
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace ExceptionHandling
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
Right click on RegisterGlobalFilters and go to its definition you will find below section where HandleErrorAttribute() is added to the FilterConfig.
using System.Web.Mvc;
namespace ExceptionHandling
{
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
}
Due to this if any error occurs it automatically navigates to the Error.cshtml page.
Now we will discuss how to handle HTTP Errors. Let’s say we are typed a wrong URL. We will get the below error page
Instead of showing this page we can show a page not found by navigating to a user friendly page. See the below code changes done in Web.config file.
Add a ErrorController in the controllers folder and add Controller Action method PageNotFound in it as follows.
using System.Web.Mvc;
namespace ExceptionHandling.Models
{
public class ErrorController : Controller
{
// GET: Error
public ActionResult PageNotFound()
{
return View();
}
}
}
Now create a view for PageNotFound in shared folder so that it can be reusable as follows.
@{
ViewBag.Title = "PageNotFound";
}
<h2>Page Not Found</h2>
Now change the code in Web.config file so that it will navigate to this page when an invalid URL is typed in the browser.
<customErrors mode="On">
<error statusCode="404" redirect="~/Error/PageNotFound"/>
</customErrors>
You can add some more satusCodes and its respective view to handle different type of HTTP Errors. At the top given some of the Client Side (400 series HTTP Error Codes) and Server Side (500 series HTTP Error Codes).
2. Handling Errors By Using Try.. Catch.. Finally Block
Now we will use our traditional try… catch… finally block to handle the errors. See the below code:
using System;
using System.Web.Mvc;
namespace ExceptionHandling.Controllers
{
public class HomeController : Controller
{
int a = 2;
int b = 0;
int result = 0;
public ActionResult Index()
{
throw new Exception();
}
public ActionResult Index1()
{
try
{
result = a / b;
}
catch (Exception ex)
{
return View("ArthimeticError");
}
finally
{
}
return View();
}
}
}
Add a ArthimeticError view into the Shared folder as follows:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Error</title>
</head>
<body>
<hgroup>
<h1>Error.</h1>
<h2>An Arthimetic Error occurred while processing your request.</h2>
</hgroup>
</body>
</html>
Now if you will navigate to the URL http://localhost:4989/home/index below is the output
If you will navigate to the URL http://localhost:4989/home/index1 below is the output due to arithmetic exception.
If you will navigate to wrong URL like http://localhost:4989/home/index12 below is the output:
3. Handling Errors by Overriding OnException Method
Now we will discuss handling the errors by overriding the OnException method. See the below code change in the same application.
using System;
using System.Web.Mvc;
namespace ExceptionHandling.Controllers
{
public class HomeController : Controller
{
int a = 2;
int b = 0;
int result = 0;
public ActionResult Index()
{
throw new Exception();
}
public ActionResult Index1()
{
try
{
result = a / b;
}
catch (Exception ex)
{
return View("ArthimeticError");
}
finally
{
}
return View();
}
public ActionResult Index2()
{
result = a / b;
return View();
}
protected override void OnException(ExceptionContext filterContext)
{
string action = filterContext.RouteData.Values["action"].ToString();
Exception e = filterContext.Exception;
filterContext.ExceptionHandled = true;
filterContext.Result = new ViewResult()
{
ViewName = "ArthimeticError"
};
}
}
}
If we will navigate to the http://localhost:4989/home/index2 URL, below is the output
4. Using [HandleError] Attribute to Handle Errors
Now we will use the [HandleError] attribute to handle the errors at controller action method level.
See the below code changes in the same application.
using System;
using System.Web.Mvc;
namespace ExceptionHandling.Controllers
{
public class HomeController : Controller
{
int a = 2;
int b = 0;
int result = 0;
public ActionResult Index()
{
throw new Exception();
}
public ActionResult Index1()
{
try
{
result = a / b;
}
catch (Exception ex)
{
return View("ArthimeticError");
}
finally
{
}
return View();
}
public ActionResult Index2()
{
result = a / b;
return View();
}
protected override void OnException(ExceptionContext filterContext)
{
string action = filterContext.RouteData.Values["action"].ToString();
Exception e = filterContext.Exception;
filterContext.ExceptionHandled = true;
filterContext.Result = new ViewResult()
{
ViewName = "ArthimeticError"
};
}
[HandleError]
public ActionResult Index3()
{
result = a / b;
return View();
}
}
}
Below is the output:
5. Handling errors by inheriting from HandleErrorAttribute
This is present in
using System.Web.Mvc;
In order to reuse error handling logic across controller we can inherit from “HandleErrorAttribute”class and decorate this class as attribute across controller.
Create a class CustomErrors by inheriting HandleErrorAttribute in models folder or in a separate folder where you can put your all reusable logics. Write the below code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace ExceptionHandling.Models
{
public class CustomErrors: HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
//if (filterContext.ExceptionHandled || filterContext.HttpContext.IsCustomErrorEnabled)
//{
// return;
//}
Exception e = filterContext.Exception;
filterContext.ExceptionHandled = true;
var model = new HandleErrorInfo(filterContext.Exception, "Controller", "Action");
filterContext.Result = new ViewResult()
{
ViewName = "CustomErrorPage",
ViewData = new ViewDataDictionary(model)
};
}
}
}
Comment the below code in Home Controller. If you will not comment it will show you arithmetic exception always as it is overriding the OnException method in Home Controller.
using ExceptionHandling.Models;
using System;
using System.Web.Mvc;
namespace ExceptionHandling.Controllers
{
public class HomeController : Controller
{
int a = 2;
int b = 0;
int result = 0;
public ActionResult Index()
{
throw new Exception();
}
public ActionResult Index1()
{
try
{
result = a / b;
}
catch (Exception)
{
return View("ArthimeticError");
}
finally
{
}
return View();
}
public ActionResult Index2()
{
result = a / b;
return View();
}
//protected override void OnException(ExceptionContext filterContext)
//{
// string action = filterContext.RouteData.Values["action"].ToString();
// Exception e = filterContext.Exception;
// filterContext.ExceptionHandled = true;
// filterContext.Result = new ViewResult()
// {
// ViewName = "ArthimeticError"
// };
//}
[HandleError]
public ActionResult Index3()
{
result = a / b;
return View();
}
[CustomErrors]
public ActionResult Index4()
{
result = a / b;
return View();
}
}
}
If you will navigate to the URL http://localhost:4989/home/index4 , the out put will be as below:
6. Global Error Handling in MVC
We can handle all types of errors globally. If any error is not caught at Controller level or Controller Action Method level, then the best place is handling all type of uncaught errors is “Global.asax” file. We can override the “Application_Error” event and do a response.redirect from the global error event. So if the error handling is not done at the controller level it will get propagated to “Global.asax” file. See the below code:
Add below code in Global.asax file:
using System;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace ExceptionHandling
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
//Server.ClearError();
// Response.Redirect("/Home/GlobalErrorPage");
}
}
}
Add GlobalErrorHandler in models folder:
using System;
using System.Web.Mvc;
namespace ExceptionHandling.Models
{
public class GlobalErrorHandler: HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
Exception e = filterContext.Exception;
filterContext.ExceptionHandled = true;
var model = new HandleErrorInfo(filterContext.Exception, "Controller", "Action");
filterContext.Result = new ViewResult()
{
ViewName = "GlobalErrorPage",
ViewData = new ViewDataDictionary(model)
};
}
}
}
Make an entry in FileterConfig Class as below:
using ExceptionHandling.Models;
using System.Web.Mvc;
namespace ExceptionHandling
{
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new GlobalErrorHandler());
}
}
}
Add few controllers which intentionally return some errors:
using ExceptionHandling.Models;
using System;
using System.Web.Mvc;
namespace ExceptionHandling.Controllers
{
public class HomeController : Controller
{
int a = 2;
int b = 0;
int result = 0;
public ActionResult Index()
{
return View();
}
public ActionResult Index1()
{
try
{
result = a / b;
}
catch (Exception)
{
return View("ArthimeticError");
}
finally
{
}
return View();
}
public ActionResult Index2()
{
result = a / b;
return View();
}
//protected override void OnException(ExceptionContext filterContext)
//{
// string action = filterContext.RouteData.Values["action"].ToString();
// Exception e = filterContext.Exception;
// filterContext.ExceptionHandled = true;
// filterContext.Result = new ViewResult()
// {
// ViewName = "ArthimeticError"
// };
//}
[HandleError]
public ActionResult Index3()
{
result = a / b;
return View();
}
[CustomErrors]
public ActionResult Index4()
{
result = a / b;
return View();
}
public ActionResult Index5()
{
result = a / b;
return View();
}
public ActionResult Index6()
{
result = a / b;
return View();
}
public ActionResult Index7()
{
result = a / b;
return View();
}
}
}
Add a view GlobalErrorPage.cshtml in shared folder :
@{
ViewBag.Title = "GlobalErrorPage";
}
<h2>Global Error Page</h2>
Now navigate to the below URL’s http://localhost:4989/home/index5
The out put is as follows and the error is handled at global level as we have not handled any error our self. See the below outputs:
No comments:
Post a Comment