ASP.NET MVC Exception Handling: 6 Methods Explained

published on 11 September 2024

Want to level up your ASP.NET MVC error handling? Here's a quick rundown of 6 powerful methods:

  1. Try-Catch Blocks: Catch specific errors in your code
  2. HandleErrorAttribute: Easy controller-level error handling
  3. Custom Error Pages: User-friendly error messages
  4. Global Error Handling: Catch all unhandled exceptions
  5. Custom Error Handling Middleware: Flexible, app-wide error management
  6. Exception Filters: Fine-grained control over exception handling

Each method has its strengths. Here's a quick comparison:

Method Best For Ease of Use Flexibility
Try-Catch Specific errors Easy Low
HandleErrorAttribute Controller errors Very Easy Medium
Custom Error Pages User experience Medium High
Global Error Handling Catching all errors Easy Medium
Custom Middleware App-wide handling Complex Very High
Exception Filters Targeted handling Medium High

Remember: Good error handling keeps your app stable, users happy, and makes debugging a breeze. Mix and match these methods for the best results.

Ready to dive in? Let's explore each method in detail.

Basics of Exception Handling in ASP.NET MVC

ASP.NET MVC

Exception handling in ASP.NET MVC isn't just about catching errors. It's about making your app rock-solid and user-friendly.

Key Exception Concepts

In .NET, exceptions are objects that pop up when things go wrong. Here's what you need to know:

  • Try-catch blocks: Your go-to for catching and handling exceptions.
  • Finally blocks: Use these for cleanup, no matter what happens.
  • Using statements: A slick way to manage resources automatically.

"Clean up resources with using statements or finally blocks. Using statements are your best bet for automatic cleanup when exceptions hit."

MVC-Specific Issues

ASP.NET MVC doesn't handle exceptions out of the box. You've got to set up your own error handling. Watch out for:

  1. Yellow Screen of Death: The default error page that spills sensitive info.
  2. Unhandled exceptions: These can crash your app if you're not careful.
  3. Route exceptions: Errors from broken links or bad URLs.

ASP.NET MVC gives you tools to tackle these:

Method Description Best For
HandleErrorAttribute Catches exceptions at the controller level Specific actions
OnException method Overridable method in controllers Controller-wide handling
Application_Error event Catches all unhandled exceptions App-wide error logging
CustomErrors in web.config Sets up custom error pages User-friendly messages

Don't just catch exceptions and do nothing. That's a recipe for trouble.

"A crashed app is easier to diagnose than one with weird, undefined behavior."

1. Using Try-Catch Blocks

Try-catch blocks are your go-to for handling exceptions in ASP.NET MVC. They let you catch and deal with errors in your controller actions.

Here's a quick example:

public ActionResult GetOrder(int id, string customerId)
{
    try
    {
        var order = _orderService.FindByCustomerAndId(id, customerId);
        return View(order);
    }
    catch (InvalidOperationException ex)
    {
        _logger.LogError(ex, "Order not found");
        return View("OrderNotFound");
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error fetching order");
        return View("Error");
    }
}

This code tries to fetch an order. If it's not found, it shows an "OrderNotFound" view. For other errors, it shows a generic error view.

Try-catch blocks have their ups and downs:

Pros Cons
Control over error handling Can clutter code
Handle specific exceptions Might catch unexpected exceptions
Custom error messages and views Can slow things down if overused
Prevents "Yellow Screen of Death" Needs careful planning

"Clean up resources with using statements or finally blocks. Using statements are your best bet for automatic cleanup when exceptions hit."

This .NET docs advice is gold. Always clean up, even when things go wrong.

Don't catch exceptions just to ignore them. That's asking for trouble. Instead, log the error, clean up, and either fix the problem or let it bubble up.

2. Using HandleErrorAttribute

HandleErrorAttribute is a filter in ASP.NET MVC that manages exceptions at the controller or action level. It's a clean way to handle errors without messy try-catch blocks.

What is HandleErrorAttribute?

It's part of System.Web.Mvc, inherits from FilterAttribute, and implements IExceptionFilter. When an exception pops up, it shows a custom error view instead of the default error page.

Here's how to use it:

[HandleError]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        // Your code here
    }
}

For specific actions, add it to the method:

[HandleError(ExceptionType = typeof(SqlException), View = "DatabaseError")]
public ActionResult GetData()
{
    // Your database code here
}

Custom Error Views

HandleErrorAttribute looks for an "Error" view in the Shared folder by default. But you can make custom views for different errors.

How to use custom error views:

  1. Create a view (e.g., "DatabaseError.cshtml") in Views/Shared.
  2. Enable custom errors in Web.config:
<system.web>
  <customErrors mode="On" defaultRedirect="Error" />
</system.web>
  1. Apply the attribute with the custom view:
[HandleError(ExceptionType = typeof(SqlException), View = "DatabaseError")]
Pros Cons
Simple to implement Only for 500-level errors
Clean controllers Needs customErrors in Web.config
Custom error views No built-in logging

"HandleErrorAttribute is great for specific error pages in MVC controllers or action methods", says a Microsoft MVP in ASP.NET.

3. Setting Up Custom Error Pages

Custom error pages in ASP.NET MVC make your app look pro and help users when things go wrong. Here's how to set them up:

Web.config Setup

Open your web.config and add this:

<system.web>
  <customErrors mode="On" defaultRedirect="~/Error/Index">
    <error statusCode="404" redirect="~/Error/NotFound"/>
    <error statusCode="500" redirect="~/Error/ServerError"/>
  </customErrors>
</system.web>

This turns on custom errors and sets pages for 404 and 500 errors.

Want to avoid 302 redirects? Use this instead:

<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx"/>

Creating User-Friendly Error Pages

Good error pages are clear, helpful, and on-brand. Here's a simple template:

<!DOCTYPE html>
<html>
<head>
    <title>Error</title>
</head>
<body>
    <h2>Oops! Something went wrong</h2>
    <p>@ViewBag.Message</p>
    <a href="/">Go back home</a>
</body>
</html>

Set the right status code like this:

<% Response.StatusCode = 404; %>

Don't forget your ErrorController:

public class ErrorController : Controller
{
    public ActionResult Index(string message)
    {
        ViewBag.Message = message;
        return View();
    }

    public ActionResult NotFound()
    {
        return View();
    }

    public ActionResult ServerError()
    {
        return View();
    }
}

With these steps, you'll have custom error pages that keep users informed and your site looking sharp.

4. Global Error Handling

Global error handling in ASP.NET MVC? It's your one-stop shop for managing errors across your entire app. Let's break it down.

Why Bother?

  • Catches ALL unhandled exceptions
  • Makes error logging a breeze
  • Keeps your error responses consistent
  • Cuts down on repeating code

How to Set It Up

Here's the deal:

1. Find your Global.asax file

2. Add this method:

protected void Application_Error(Object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    // Log the error
    // Redirect to custom error page
}

3. Inside, you can:

  • Log error details
  • Send out error notifications
  • Send users to a custom error page

Want a more detailed example? Check this out:

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

    Logger.LogError(ex);
    Server.ClearError();
    Response.Redirect("/Home/Error");
}

This setup? It's like a safety net for your app. Catches all those pesky unhandled exceptions, logs them, and shows users a nice error page.

Using ASP.NET Core? No problem. Use this built-in middleware:

app.UseExceptionHandler("/Home/Error");

Pop that in your Startup.cs file. It'll catch exceptions and redirect to your error route.

Don't forget to add this to your HomeController:

public IActionResult Error()
{
    return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}

And there you have it. Global error handling, simplified.

sbb-itb-29cd4f6

5. Custom Error Handling Middleware

Want better exception handling? Custom middleware is your answer.

Creating Custom Middleware

Here's how to build your own safety net:

  1. Make an ExceptionMiddleware class
  2. Add a constructor with RequestDelegate and ILogger
  3. Implement InvokeAsync to catch exceptions

Here's a quick example:

public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionMiddleware> _logger;

    public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unhandled exception occurred.");
            await HandleExceptionAsync(context, ex);
        }
    }

    private Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;

        return context.Response.WriteAsync(new ErrorDetails()
        {
            StatusCode = context.Response.StatusCode,
            Message = "Internal Server Error."
        }.ToString());
    }
}

public class ErrorDetails
{
    public int StatusCode { get; set; }
    public string Message { get; set; }

    public override string ToString()
    {
        return JsonSerializer.Serialize(this);
    }
}

This middleware catches unhandled exceptions, logs them, and sends a JSON response.

Using Your New Middleware

To put it to work:

  1. Open Program.cs (or Startup.cs for older versions)
  2. Add this line before other middleware:
app.UseMiddleware<ExceptionMiddleware>();

That's it. Your app now has custom exception handling.

But why stop there? Make your middleware smarter:

  • Log to a database for later analysis
  • Set up email or Slack alerts for critical errors
  • Return different status codes based on exception type

6. Using Exception Filters

Exception filters in ASP.NET MVC help you handle errors across your app. They catch and process exceptions before users see them.

Creating Exception Filters

To make an exception filter:

  1. Implement IExceptionFilter
  2. Override OnException
  3. Add error handling logic

Here's a simple example:

public class CustomExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        Console.WriteLine($"Error: {filterContext.Exception.Message}");
        filterContext.Result = new ViewResult { ViewName = "Error" };
        filterContext.ExceptionHandled = true;
    }
}

This filter logs the error and shows an "Error" view.

Using Filters in Your App

Apply filters:

  1. Globally
  2. To controllers
  3. To actions

For global use, add to FilterConfig.cs:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomExceptionFilter());
    }
}

For controllers:

[CustomExceptionFilter]
public class HomeController : Controller
{
    // Controller actions
}

For actions:

public class HomeController : Controller
{
    [CustomExceptionFilter]
    public ActionResult Index()
    {
        // Action logic
    }
}

Exception filters offer these benefits:

Benefit Description
Centralized handling Manage errors in one place
Flexibility Apply filters where needed
Customization Tailor error responses
Logging Easily add error logging

Tips for Good Exception Handling

How to Log Errors Well

Logging errors is crucial for debugging your ASP.NET MVC app. Here's how to do it right:

Use a logging framework like Serilog or NLog. Log at the right level and include context.

Log.Error(ex, "Failed to process order {OrderId} for user {UserId}", orderId, userId);

Writing Clear Error Messages

Users need to understand what went wrong. Be specific, avoid jargon, and suggest next steps if possible.

Good Bad
"Can't connect to database. Try again later." "Error 500: Internal Server Error"
"Email address already in use." "Validation failed for email field"

Keeping Your App Secure

Don't expose sensitive info in error messages. Use custom error pages and log securely.

Set up custom error pages in Web.config:

<customErrors mode="On" defaultRedirect="~/Error/General">
    <error statusCode="404" redirect="~/Error/NotFound"/>
    <error statusCode="500" redirect="~/Error/ServerError"/>
</customErrors>

Good exception handling is a balancing act. Log enough to debug, but not so much that you risk exposing sensitive data.

Comparing the 6 Methods

Let's break down how these exception handling methods stack up in ASP.NET MVC:

Method Pros Cons
Try-Catch Blocks Simple, localized handling Can clutter code, may slow things down
HandleErrorAttribute Easy global setup, custom views Limited to 500 errors, misses child actions
Custom Error Pages User-friendly, tailored messages Needs web.config setup, only for unhandled exceptions
Global Error Handling Catches all unhandled exceptions More complex, might hide details
Custom Error Handling Middleware Flexible, reusable Extra coding, adds complexity
Exception Filters Fine-grained control Can get messy if overused

Each method has its sweet spot. Here's how they work in the real world:

1. Try-Catch Blocks

Perfect for known, specific exceptions in critical spots:

try
{
    var result = RiskyStuff();
}
catch (SpecificException ex)
{
    Log.Error(ex, "RiskyStuff went wrong");
    // Handle it
}

2. HandleErrorAttribute

Quick and easy default error handling:

[HandleError(View = "Oops")]
public class HomeController : Controller
{
    // Your controller stuff
}

3. Custom Error Pages

For a smooth user experience when things go south:

<customErrors mode="On" defaultRedirect="~/Error/General">
    <error statusCode="404" redirect="~/Error/Lost"/>
    <error statusCode="500" redirect="~/Error/Boom"/>
</customErrors>

4. Global Error Handling

Catch-all for unhandled exceptions:

protected void Application_Error(object sender, EventArgs e)
{
    var oops = Server.GetLastError();
    Log.Error(oops, "Something went wrong");
    // Deal with it
}

5. Custom Error Handling Middleware

Flexible but needs more setup:

public class ErrorMiddleware
{
    private readonly RequestDelegate _next;

    public ErrorMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await HandleIt(context, ex);
        }
    }

    private Task HandleIt(HttpContext context, Exception ex)
    {
        // Log and handle the mess
    }
}

6. Exception Filters

Good for specific exceptions across multiple controllers:

public class CustomExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        if (context.Exception is CustomException)
        {
            // Handle your custom exception
        }
    }
}

In the real world, mixing these methods often works best. Use global handling for logging, custom pages for user-friendly messages, and try-catch for specific scenarios.

Wrap-up

We've covered six exception handling methods in ASP.NET MVC. Here's what you need to know:

Key Takeaways

1. Combine methods: Use global handling for logging, custom pages for user messages, and try-catch for specific cases.

2. Log thoroughly: Include all relevant details when logging errors. Scott Hanselman from Microsoft says:

"Log everything you can. You never know what will be useful later."

3. Put users first: Give clear, helpful error messages without revealing sensitive info.

4. Pick the right tool: Each method has its strengths:

Method Best For
Try-Catch Known exceptions in critical code
HandleErrorAttribute Quick, default handling
Custom Error Pages User-friendly messages
Global Error Handling Catching all unhandled exceptions
Custom Error Middleware Flexible, reusable logic
Exception Filters Specific exceptions across controllers

5. Stay secure: Don't expose sensitive data in errors or logs.

6. Use external tools: Consider services like Sentry or Rollbar for better error management.

FAQs

How to handle exceptions in ASP.NET MVC?

Want to manage exceptions in ASP.NET MVC? Override the OnException() method in your controller class. It's simple and effective.

Here's why it's great:

  • Catches all unhandled 500 errors
  • Let's you log exceptions
  • Redirects to specific views
  • No need to mess with <customErrors> in web.config

Check out this example:

public class HomeController : Controller
{
    protected override void OnException(ExceptionContext filterContext)
    {
        filterContext.ExceptionHandled = true;
        // Log the error here
        filterContext.Result = RedirectToAction("Error", "Home");
    }
}

This code catches any unhandled exception, marks it as handled, and sends users to an error page. You can tweak it to add logging or show friendly messages.

Using this method keeps your error handling clean and centralized. No need for try-catch blocks all over your action methods.

Related posts

Read more