Thursday, August 9, 2012

Learning MVC: Display a Custom Error Page, Log Error and Send Email

Step one - create my own controller class. Simple, just add a BaseController to the Controllers folder

public abstract class BaseController : Controller
{
}

and then modify all existing contollers to inherit from BaseController rather than from System.Web.Mvc.Controller.

Next, I override the OnException method in the BaseController which is called whenever the exception is thrown within an action.

public abstract class BaseController : Controller
{
    protected override void OnException(ExceptionContext filterContext)
    {
        var fileName = Path.Combine(Request.MapPath("~/App_Data"), "log.txt");
        WriteLog(fileName, filterContext.Exception.ToString());
        filterContext.ExceptionHandled = true; 
        this.View("Error").ExecuteResult(this.ControllerContext);
    }
    
    static void WriteLog(string logFile, string text)
    {   
        StringBuilder message = new StringBuilder();
        message.AppendLine(DateTime.Now.ToString());
        message.AppendLine(text);
        message.AppendLine("=========================================");
        System.IO.File.AppendAllText(logFile, message.ToString());
    }
}

I can verify and find out that the Yellow Screen of Death is not indeed shown

Custom Error Page

And the log file is in my App_Data folder

Log File

Now that I can see it works, I would still want to see the exceptions as soon as they occur, rather than checking the log file on each occasion. To achieve that, first I need to add

<customErrors mode="RemoteOnly" />

to the system.web section of the Web.config file and then in the OnException method check if this section is set.

protected override void OnException(ExceptionContext filterContext)
{
    var fileName = Path.Combine(Request.MapPath("~/App_Data"), "log.txt");
    WriteLog(fileName, filterContext.Exception.ToString());
    if (filterContext.HttpContext.IsCustomErrorEnabled)
    {
        filterContext.ExceptionHandled = true; 
        this.View("Error").ExecuteResult(this.ControllerContext);
    }
}

Finally, I would like to receive an email when something on my website goes wrong. I'm adding a function for that, rearranging a few lines and come up with the final version of my BaseController (for now).

using System;
using System.Web.Mvc;
using System.Text;
using System.IO;
using System.Net.Mail;

namespace Recipes.Controllers
{
    public abstract class BaseController : Controller
    {
        protected override void OnException(ExceptionContext filterContext)
        {
            string ex = filterContext.Exception.ToString();
            var fileName = Path.Combine(Request.MapPath("~/App_Data"), "log.txt");
            WriteLog(fileName, ex);
            SendEmail(ex);
            if (filterContext.HttpContext.IsCustomErrorEnabled)
            {
                filterContext.ExceptionHandled = true; 
                this.View("Error").ExecuteResult(this.ControllerContext);
            }
        }

        static StringBuilder ErrorText(string text)
        {
            StringBuilder message = new StringBuilder();
            message.AppendLine(DateTime.Now.ToString());
            message.AppendLine(text);
            message.AppendLine("=========================================");
            return message;
        }

        static void WriteLog(string logFile, string text)
        {
            System.IO.File.AppendAllText(logFile, ErrorText(text).ToString());
        }

        static void SendEmail(string text)
        {
            MailMessage mail = new MailMessage();
            SmtpClient client = new SmtpClient("smtp.example.com");
            client.Credentials = new System.Net.NetworkCredential("u$3r", "pa$$word"); client.Port = 587;

            mail.From = new MailAddress("mvc@example.com");
            mail.To.Add("developer@example.com");
            mail.Subject = "Error on your website";
            mail.Body = ErrorText(text).ToString();

            client.Send(mail); 
        }
    }
}

References

Problem with generic base controller error handling in ASP.NET MVC
ASP.NET MVC HandleError Attribute, Custom Error Pages and Logging Exceptions
How to send email from Asp.net Mvc-3?
by . Also posted on my website

No comments: