ASP.NET Core
HttpException
ASP.NET MVC 5
Error Handling
Exception Handling

ASP.NET Core equivalent of ASP.NET MVC 5's HttpException

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

ASP.NET MVC 5 had HttpException for returning HTTP error status codes from controllers. ASP.NET Core removed this class entirely. Instead, you return status code results directly from controllers (return NotFound(), return BadRequest()), throw custom exceptions handled by middleware, or use the built-in ProblemDetails response format. The shift reflects ASP.NET Core's middleware-based architecture where HTTP responses are not driven by exceptions but by explicit result objects.

The Old Way: ASP.NET MVC 5

csharp
1// ASP.NET MVC 5 — throw HttpException to return an HTTP error
2public ActionResult GetUser(int id)
3{
4    var user = _userService.Find(id);
5    if (user == null)
6        throw new HttpException(404, "User not found");
7
8    return View(user);
9}

HttpException would be caught by the framework and converted into the corresponding HTTP status code response. This class does not exist in ASP.NET Core.

ASP.NET Core controllers have built-in helper methods for every common HTTP status:

csharp
1[ApiController]
2[Route("api/[controller]")]
3public class UsersController : ControllerBase
4{
5    [HttpGet("{id}")]
6    public IActionResult GetUser(int id)
7    {
8        var user = _userService.Find(id);
9        if (user == null)
10            return NotFound(new { message = "User not found" });
11
12        return Ok(user);
13    }
14
15    [HttpPost]
16    public IActionResult CreateUser(CreateUserDto dto)
17    {
18        if (!ModelState.IsValid)
19            return BadRequest(ModelState);
20
21        var user = _userService.Create(dto);
22        return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
23    }
24}

Available helper methods:

MethodStatus CodeUse Case
Ok()200Successful response
Created()201Resource created
NoContent()204Successful with no body
BadRequest()400Invalid input
Unauthorized()401Not authenticated
Forbid()403Not authorized
NotFound()404Resource missing
Conflict()409State conflict
StatusCode(n)AnyCustom status code

Fix 2: Custom Exception with Middleware

Create your own HttpException replacement and handle it globally with middleware:

csharp
1// Custom exception class
2public class ApiException : Exception
3{
4    public int StatusCode { get; }
5
6    public ApiException(int statusCode, string message) : base(message)
7    {
8        StatusCode = statusCode;
9    }
10}
11
12// Global exception handling middleware
13public class ExceptionMiddleware
14{
15    private readonly RequestDelegate _next;
16
17    public ExceptionMiddleware(RequestDelegate next)
18    {
19        _next = next;
20    }
21
22    public async Task InvokeAsync(HttpContext context)
23    {
24        try
25        {
26            await _next(context);
27        }
28        catch (ApiException ex)
29        {
30            context.Response.StatusCode = ex.StatusCode;
31            context.Response.ContentType = "application/json";
32            await context.Response.WriteAsJsonAsync(new
33            {
34                error = ex.Message,
35                statusCode = ex.StatusCode
36            });
37        }
38    }
39}
40
41// Register in Program.cs
42app.UseMiddleware<ExceptionMiddleware>();

Now you can throw exceptions like the old MVC 5 pattern:

csharp
1public IActionResult GetUser(int id)
2{
3    var user = _userService.Find(id)
4        ?? throw new ApiException(404, "User not found");
5
6    return Ok(user);
7}

Fix 3: UseExceptionHandler with ProblemDetails

ASP.NET Core 7+ has built-in ProblemDetails support:

csharp
1// Program.cs
2builder.Services.AddProblemDetails();
3
4app.UseExceptionHandler();
5app.UseStatusCodePages();

This automatically converts unhandled exceptions and error status codes into RFC 7807 ProblemDetails JSON:

json
1{
2    "type": "https://tools.ietf.org/html/rfc9110#section-15.5.5",
3    "title": "Not Found",
4    "status": 404,
5    "traceId": "00-abc123..."
6}

Fix 4: Exception Filters

For controller-scoped exception handling:

csharp
1public class ApiExceptionFilter : IExceptionFilter
2{
3    public void OnException(ExceptionContext context)
4    {
5        if (context.Exception is ApiException apiEx)
6        {
7            context.Result = new ObjectResult(new { error = apiEx.Message })
8            {
9                StatusCode = apiEx.StatusCode
10            };
11            context.ExceptionHandled = true;
12        }
13    }
14}
15
16// Register globally in Program.cs
17builder.Services.AddControllers(options =>
18{
19    options.Filters.Add<ApiExceptionFilter>();
20});

Comparison of Approaches

ApproachScopeBest For
Return NotFound() etc.Per actionAPI controllers, simple cases
Custom middlewareGlobalConsistent error format across all endpoints
ProblemDetailsGlobalStandards-compliant REST APIs
Exception filtersController/globalMVC apps needing controller-level handling

Common Pitfalls

  • Throwing exceptions for control flow: Returning NotFound() is cheaper than throwing an exception. Exceptions have stack trace overhead. Reserve exceptions for truly exceptional situations, not expected cases like "record not found."
  • Missing [ApiController] attribute: Without it, ASP.NET Core does not automatically return 400 for invalid ModelState. Add [ApiController] to all API controllers.
  • Swallowing exception details in production: Never expose stack traces or internal details in production responses. Use ProblemDetails with environment-aware detail levels.
  • Forgetting UseExceptionHandler order: Middleware order matters. UseExceptionHandler() must be registered early in the pipeline (before UseRouting()) to catch exceptions from downstream middleware.
  • Using HttpContext.Response.StatusCode after response started: Once headers are sent, you cannot change the status code. Exception middleware must check context.Response.HasStarted before writing.

Summary

  • ASP.NET Core removed HttpException — use controller helper methods (NotFound(), BadRequest()) instead
  • For throw-and-catch patterns, create a custom exception class with global exception middleware
  • Use ProblemDetails (ASP.NET Core 7+) for RFC 7807 compliant error responses
  • Exception filters provide controller-scoped handling for MVC applications
  • Prefer returning status code results over throwing exceptions for expected error conditions

Course illustration
Course illustration

All Rights Reserved.