Introduction
There are some limitations to the Exception Filter.
- The error won’t get logged anywhere.
- Exceptions raised outside controllers will not be handled. Example- exception raised because of invalid URL won’t be handled.
- Exception Handling based on the scenario is not possible. Example – So one error page when the request comes via AJAX and a different one when comes via normal request.
We will overcome all these problems by creating a Custom Exception Filter in MVC that means we will create a Custom Handle Error Filter in MVC application.
Step 1
Create a database table in SQL Server 2014 or a version of your choice.
Employee Table
- CREATE TABLE [dbo].[Employee](
- [Id] [int] IDENTITY(1,1) NOT NULL,
- [Name] [nvarchar](50) NULL,
- [Gender] [char](10) NULL,
- [Age] [int] NULL,
- [Position] [nvarchar](50) NULL,
- [Office] [nvarchar](50) NULL,
- [Salary] [int] NULL,
- CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED
- (
- [Id] ASC
- )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
- ) ON [PRIMARY]
-
- GO
ExceptionLogger Table
- CREATE TABLE [dbo].[ExceptionLogger](
- [ExceptionId] [int] IDENTITY(1,1) NOT NULL,
- [ExceptionMessage] [nvarchar](max) NULL,
- [ControllerName] [nvarchar](100) NULL,
- [ActionName] [nvarchar](100) NULL,
- [ExceptionStackTrack] [nvarchar](max) NULL,
- [ExceptionLogTime] [datetime] NULL,
- CONSTRAINT [PK_ExceptionLogger] PRIMARY KEY CLUSTERED
- (
- [ExceptionId] ASC
- )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
- ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
-
- GO
Step 2
Open Visual Studio 2015 or an editor of your choice and create a new project.
Step 3
Choose the web application project and give an appropriate name to your project.
Step 4
Select empty template, check on MVC checkbox below, and click OK.
Step 5
Right-click on the Models folder and add a database model. Add Entity Framework now. For that, right-click on Models folder, select Add, then select "New Item".
You will get a window; from there, select Data from the left panel and choose ADO.NET Entity Data Model. Give the name as EmployeeModel (this name is not mandatory, you can give any name) and click "Add".
After you click on "Add a window", the wizard will open. Choose EF Designer from the database and click "Next".
After clicking on "Next", a window will appear. Choose New Connection. Another window will appear. Add your server name - if it is local, then enter a dot (.). Choose your database and click "OK".
The connection will be added. If you wish, save the connection name as you want. You can change the name of your connection below. It will save the connection in the web config. Now, click "Next"
After clicking on NEXT, another window will appear. Choose the database table name as shown in the below screenshot and click "Finish".
Entity Framework gets added and the respective class gets generated under the Models folder.
Employee Class
- using System.ComponentModel.DataAnnotations;
-
- namespace MvcCustomExceptionFilter.Models
- {
- using System;
- using System.Collections.Generic;
-
- public partial class Employee
- {
- public int Id { get; set; }
-
- [Required(ErrorMessage = "Please enter name")]
- public string Name { get; set; }
-
- [Required(ErrorMessage = "Please choose gender")]
- public string Gender { get; set; }
-
- [Required(ErrorMessage = "Please enter age")]
- public Nullable<int> Age { get; set; }
-
- [Required(ErrorMessage = "Please enter position")]
- public string Position { get; set; }
-
- [Required(ErrorMessage = "Please enter you office location")]
- public string Office { get; set; }
-
- [Required(ErrorMessage = "Please enter your salary")]
- public Nullable<int> Salary { get; set; }
- }
- }
Step 6
Right click on controllers folder and add controller.
A window will appear. Choose MVC5 Controller-Empty and click "Add".
After clicking on "Add", another window will appear with DefaultController. Change the name to HomeController and click "Add". The HomeController will be added under the Controllers folder. Don’t change the Controller suffix for all controllers, change only the highlight, and instead of Default, just change Home;
Complete code for Home Controller
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using MvcCustomExceptionFilter.CustomFilter;
- using MvcCustomExceptionFilter.Models;
-
- namespace MvcCustomExceptionFilter.Controllers
- {
- public class HomeController : Controller
- {
- private readonly EmployeeContext _dbContext=new EmployeeContext();
-
- public ActionResult Index()
- {
- var employee = _dbContext.Employees.ToList();
- return View(employee);
- }
-
- public ActionResult Create()
- {
- return View();
- }
-
- [HttpPost]
- [CustomExceptionHandlerFilter]
- public ActionResult Create(Employee employee)
- {
- if (ModelState.IsValid)
- {
- if (employee.Position == "Software Engineer" && (employee.Salary < 50000 || employee.Salary > 80000))
- {
- throw new Exception("Salary is not matching with Software Engineer position.");
- }
- else if (employee.Position == "Accountant" && (employee.Salary < 40000 || employee.Salary > 50000))
- {
- throw new Exception("Salary is not matching with Accountant position.");
- }
- else if (employee.Position == "Senior Sales Executive")
- {
- throw new Exception("Salary is not matching with Senior Sales Executive position.");
- }
- else
- {
- _dbContext.Employees.Add(employee);
- _dbContext.SaveChanges();
- }
- }
- return RedirectToAction("Index");
- }
- }
- }
Step 7
Right click on Index method in HomeController The "Add View" window will appear with default index name checked (use a Layout page), and click on "Add.
Code for Index View
- @model IEnumerable<MvcCustomExceptionFilter.Models.Employee>
-
- @{
- ViewBag.Title = "Index";
- }
-
- @Html.ActionLink("Add New","Create","Home",new {@class="btn btn-primary",style="margin-top:10px; margin-bottom:10px;"})
-
- <table class="table table-bordered">
- <thead>
- <tr>
- <th>@Html.DisplayNameFor(m=>m.Name)</th>
- <th>@Html.DisplayNameFor(m=>m.Gender)</th>
- <th>@Html.DisplayNameFor(m=>m.Age)</th>
- <th>@Html.DisplayNameFor(m=>m.Position)</th>
- <th>@Html.DisplayNameFor(m=>m.Office)</th>
- <th>@Html.DisplayNameFor(m=>m.Salary)</th>
- </tr>
- </thead>
- @foreach (var emp in Model)
- {
- <tr>
- <td>@emp.Name</td>
- <td>@emp.Gender</td>
- <td>@emp.Age</td>
- <td>@emp.Position</td>
- <td>@emp.Office</td>
- <td>@emp.Salary</td>
- </tr>
- }
- </table>
Code for Creating view
- @model MvcCustomExceptionFilter.Models.Employee
- @{
- ViewBag.Title = "Create";
- }
-
- <h2>Create</h2>
-
- @using (Html.BeginForm())
- {
- <div class="form-group">
- @Html.LabelFor(m => m.Name)
- @Html.TextBoxFor(m => m.Name, new { @class = "form-control" })
- @Html.ValidationMessageFor(m => m.Name)
- </div>
- <div class="form-group">
- @Html.LabelFor(m => m.Name)
- @Html.DropDownList("Gender",new List<SelectListItem>()
- {
- new SelectListItem {Text = "Male",Value = "1"},
- new SelectListItem {Text = "Female", Value = "2"}
- },new {@class="form-control"})
-
- @Html.ValidationMessageFor(m => m.Gender)
- </div>
- <div class="form-group">
- @Html.LabelFor(m => m.Age)
- @Html.TextBoxFor(m => m.Age, new { @class = "form-control" })
- @Html.ValidationMessageFor(m => m.Age)
- </div>
- <div class="form-group">
- @Html.LabelFor(m => m.Position)
- @Html.TextBoxFor(m => m.Position, new { @class = "form-control" })
- @Html.ValidationMessageFor(m => m.Position)
- </div>
- <div class="form-group">
- @Html.LabelFor(m => m.Office)
- @Html.TextBoxFor(m => m.Office, new { @class = "form-control" })
- @Html.ValidationMessageFor(m => m.Office)
- </div>
- <div class="form-group">
- @Html.LabelFor(m => m.Salary)
- @Html.TextBoxFor(m => m.Salary, new { @class = "form-control" })
- @Html.ValidationMessageFor(m => m.Salary)
- </div>
- <div class="form-group">
- <button type="submit" class="btn btn-primary">Submit</button>
- </div>
- }
Step 8
Right click on project create a folder CustomFilter add a class with name CustomExceptionHandlerFilter.cs,
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using MvcCustomExceptionFilter.Models;
-
- namespace MvcCustomExceptionFilter.CustomFilter
- {
- public class CustomExceptionHandlerFilter:FilterAttribute,IExceptionFilter
- {
- public void OnException(ExceptionContext filterContext)
- {
- ExceptionLogger logger=new ExceptionLogger()
- {
- ExceptionMessage = filterContext.Exception.Message,
- ExceptionStackTrack = filterContext.Exception.StackTrace,
- ControllerName = filterContext.RouteData.Values["controller"].ToString(),
- ActionName = filterContext.RouteData.Values["action"].ToString(),
- ExceptionLogTime = DateTime.Now
- };
- EmployeeContext dbContext=new EmployeeContext();
- dbContext.ExceptionLoggers.Add(logger);
- dbContext.SaveChanges();
-
- filterContext.ExceptionHandled = true;
- filterContext.Result=new ViewResult()
- {
- ViewName = "Error"
- };
- }
- }
- }
Step 9
Create an error view under shared folder.
- @{
- ViewBag.Title = "Error";
- }
-
- <hgroup>
- <h1>Error Occured </h1>
- <h1 class="alert alert-danger">An unknown error occurred while processing your request.</h1>
- </hgroup>
Step 10
Enable custom errors in the web.config file that is present in the root directory of MVC application. customErrors element must be nested under <system.web>.
- <customErrors mode="On"></customErrors>
Step 11
Create a class FilterConfig under App_Start folder if don’t have.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using Microsoft.ApplicationInsights.DataContracts;
- using MvcCustomExceptionFilter.CustomFilter;
-
- namespace MvcCustomExceptionFilter.App_Start
-
- {
- public class FilterConfig
- {
- public static void RegisterGlobalFilter(GlobalFilterCollection filters)
- {
- filters.Add(new CustomExceptionHandlerFilter());
- }
- }
- }
Step 12
Now, register your custom exception filter in Global.asax.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using System.Web.Routing;
- using MvcCustomExceptionFilter.App_Start;
-
- namespace MvcCustomExceptionFilter
- {
- public class MvcApplication : System.Web.HttpApplication
- {
- protected void Application_Start()
- {
- FilterConfig.RegisterGlobalFilter(GlobalFilters.Filters);
- AreaRegistration.RegisterAllAreas();
- RouteConfig.RegisterRoutes(RouteTable.Routes);
- }
- }
- }
Step 13
Build and run you project ctrl+F5.